feat(dashboard): aquarium 2D visualization (fish per agent, size by fitness)
Nuova pagina Streamlit "Aquarium" che renderizza ogni genoma come pesce animato su canvas HTML5: dimensione proporzionale alla fitness, colore per cognitive_style, halo per i top-3. Helper puro-Python testabile per costruire dataset e HTML self-contained (no CDN). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import importlib
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def test_streamlit_app_imports():
|
||||
importlib.import_module("multi_swarm.dashboard.data")
|
||||
@@ -12,3 +14,74 @@ def test_dashboard_data_helpers_signatures():
|
||||
assert hasattr(data, "generations_df")
|
||||
assert hasattr(data, "evaluations_df")
|
||||
assert hasattr(data, "genomes_df")
|
||||
|
||||
|
||||
def test_aquarium_helper_builds_html():
|
||||
from multi_swarm.dashboard.aquarium import build_aquarium_html
|
||||
|
||||
fish = [
|
||||
{
|
||||
"id": "abc123",
|
||||
"fitness": 0.8,
|
||||
"cognitive_style": "physicist",
|
||||
"n_trades": 30,
|
||||
"dsr": 0.7,
|
||||
},
|
||||
{
|
||||
"id": "def456",
|
||||
"fitness": 0.0,
|
||||
"cognitive_style": "biologist",
|
||||
"n_trades": 0,
|
||||
"dsr": 0.0,
|
||||
},
|
||||
]
|
||||
html = build_aquarium_html(fish, canvas_w=800, canvas_h=400, show_labels=True)
|
||||
assert "canvas" in html
|
||||
assert "abc123" in html
|
||||
assert "physicist" in html or "4cc9f0" in html
|
||||
assert "requestAnimationFrame" in html
|
||||
|
||||
|
||||
def test_aquarium_build_fish_dataset_sorts_and_caps():
|
||||
from multi_swarm.dashboard.aquarium import build_fish_dataset
|
||||
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"genome_id": "low", "fitness": 0.1, "cognitive_style": "physicist",
|
||||
"n_trades": 1, "dsr": 0.0},
|
||||
{"genome_id": "high", "fitness": 0.9, "cognitive_style": "biologist",
|
||||
"n_trades": 10, "dsr": 0.5},
|
||||
{"genome_id": "mid", "fitness": 0.5, "cognitive_style": "engineer",
|
||||
"n_trades": 5, "dsr": 0.2},
|
||||
]
|
||||
)
|
||||
out = build_fish_dataset(df, max_fish=2)
|
||||
assert len(out) == 2
|
||||
assert out[0]["id"] == "high"
|
||||
assert out[1]["id"] == "mid"
|
||||
assert out[0]["cognitive_style"] == "biologist"
|
||||
|
||||
|
||||
def test_aquarium_build_fish_dataset_drops_nan_fitness():
|
||||
from multi_swarm.dashboard.aquarium import build_fish_dataset
|
||||
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"genome_id": "ok", "fitness": 0.4, "cognitive_style": "historian",
|
||||
"n_trades": 2, "dsr": 0.1},
|
||||
{"genome_id": "bad", "fitness": float("nan"), "cognitive_style": "ecologist",
|
||||
"n_trades": 0, "dsr": 0.0},
|
||||
]
|
||||
)
|
||||
out = build_fish_dataset(df)
|
||||
assert len(out) == 1
|
||||
assert out[0]["id"] == "ok"
|
||||
|
||||
|
||||
def test_aquarium_empty_input_returns_empty():
|
||||
from multi_swarm.dashboard.aquarium import build_aquarium_html, build_fish_dataset
|
||||
|
||||
assert build_fish_dataset(pd.DataFrame()) == []
|
||||
html = build_aquarium_html([], canvas_w=400, canvas_h=200)
|
||||
assert "canvas" in html
|
||||
assert "Acquario vuoto" in html
|
||||
|
||||
Reference in New Issue
Block a user