LBJLincoln Claude Opus 4.6 commited on
Commit
c8c2c05
Β·
1 Parent(s): 2ed14b5

feat: multi-island evolution + Codespace runner

Browse files

- app.py: env-overridable SPACE_ROLE config (6 roles: exploitation, exploration,
extra_trees_specialist, catboost_specialist, neural_specialist, wide_search)
- deploy_island.py: generic deploy to any HF Space with any role
- deploy_s11.py: S11 exploration island deploy script
- .devcontainer/: GitHub Codespace config + evolution runner script
- S12 (extra_trees) + S13 (catboost) deployed on LBJLincoln26 account

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (3) hide show
  1. app.py +47 -12
  2. deploy_island.py +101 -0
  3. deploy_s11.py +113 -0
app.py CHANGED
@@ -1566,21 +1566,56 @@ def _ece(probs, actuals, n_bins=10):
1566
  # EVOLUTION ENGINE
1567
  # ═══════════════════════════════════════════════════════
1568
 
1569
- POP_SIZE = 60 # Optimal: 60 (proven by 1244 experiments β€” fewer=faster gens/day)
1570
- N_ISLANDS = 5 # Island model: 5 sub-populations
1571
- ISLAND_SIZE = 12 # 60/5 = 12 per island
1572
- ELITE_SIZE = 6 # Top 10% preserved
1573
- ELITE_PER_ISLAND = 2 # Elites per island (ceil(12*0.1)=2)
1574
- BASE_MUT = 0.09 # Optimal: 0.09 (0.2 destroys population, 0.12 too high)
1575
- MUT_DECAY_RATE = 0.998 # Gentle decay β€” already near optimum
1576
- MUT_FLOOR = 0.05 # Don't go too low β€” maintain exploration
1577
- CROSSOVER_RATE = 0.80 # Optimal: 0.80 (0.85-0.95 too much noise)
1578
- TARGET_FEATURES = 63 # Sweet spot: 60-66 range dominates (not 80/115)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1579
  N_SPLITS = 3 # 3-fold walk-forward for reliable Brier estimates
1580
  GENS_PER_CYCLE = 3 # Save every 3 gens
1581
  COOLDOWN = 5 # Minimal cooldown
1582
- TOURNAMENT_SIZE = 7 # Higher pressure with small pop β€” proven optimal
1583
- DIVERSITY_RESTART = 30 # More patience with small pop
1584
  FAST_EVAL_GAMES = 8000 # More data in fast eval
1585
  FULL_EVAL_TOP = 10 # Full eval for top 10 (out of 60)
1586
  MIGRATION_INTERVAL = 8 # Migration every 8 gens
 
1566
  # EVOLUTION ENGINE
1567
  # ═══════════════════════════════════════════════════════
1568
 
1569
+ # ── Runtime config (env-overridable for multi-Space island model) ──
1570
+ # S10 (primary): exploitation β€” proven optimal params
1571
+ # S11 (secondary): exploration β€” higher mutation, wider feature search
1572
+ import os as _os
1573
+ _SPACE_ROLE = _os.environ.get("SPACE_ROLE", "exploitation") # "exploitation" or "exploration"
1574
+
1575
+ _ROLE_CONFIGS = {
1576
+ "exploitation": { # S10: proven optimal
1577
+ "POP_SIZE": 60, "BASE_MUT": 0.09, "MUT_DECAY_RATE": 0.998, "MUT_FLOOR": 0.05,
1578
+ "CROSSOVER_RATE": 0.80, "TARGET_FEATURES": 63, "TOURNAMENT_SIZE": 7, "DIVERSITY_RESTART": 30,
1579
+ },
1580
+ "exploration": { # S11: wider search
1581
+ "POP_SIZE": 60, "BASE_MUT": 0.15, "MUT_DECAY_RATE": 0.999, "MUT_FLOOR": 0.08,
1582
+ "CROSSOVER_RATE": 0.70, "TARGET_FEATURES": 80, "TOURNAMENT_SIZE": 5, "DIVERSITY_RESTART": 20,
1583
+ },
1584
+ "extra_trees_specialist": { # S12: extra_trees focus, tight features
1585
+ "POP_SIZE": 60, "BASE_MUT": 0.08, "MUT_DECAY_RATE": 0.998, "MUT_FLOOR": 0.04,
1586
+ "CROSSOVER_RATE": 0.80, "TARGET_FEATURES": 60, "TOURNAMENT_SIZE": 7, "DIVERSITY_RESTART": 25,
1587
+ },
1588
+ "catboost_specialist": { # S13: catboost focus, medium exploration
1589
+ "POP_SIZE": 60, "BASE_MUT": 0.10, "MUT_DECAY_RATE": 0.998, "MUT_FLOOR": 0.05,
1590
+ "CROSSOVER_RATE": 0.80, "TARGET_FEATURES": 66, "TOURNAMENT_SIZE": 6, "DIVERSITY_RESTART": 25,
1591
+ },
1592
+ "neural_specialist": { # S14: neural models (MLP, TabNet, FT-Transformer)
1593
+ "POP_SIZE": 40, "BASE_MUT": 0.12, "MUT_DECAY_RATE": 0.999, "MUT_FLOOR": 0.06,
1594
+ "CROSSOVER_RATE": 0.75, "TARGET_FEATURES": 50, "TOURNAMENT_SIZE": 5, "DIVERSITY_RESTART": 15,
1595
+ },
1596
+ "wide_search": { # S15: max diversity, wide feature search
1597
+ "POP_SIZE": 60, "BASE_MUT": 0.20, "MUT_DECAY_RATE": 0.999, "MUT_FLOOR": 0.10,
1598
+ "CROSSOVER_RATE": 0.65, "TARGET_FEATURES": 120, "TOURNAMENT_SIZE": 4, "DIVERSITY_RESTART": 15,
1599
+ },
1600
+ }
1601
+ _cfg = _ROLE_CONFIGS.get(_SPACE_ROLE, _ROLE_CONFIGS["exploitation"])
1602
+ POP_SIZE = _cfg["POP_SIZE"]
1603
+ BASE_MUT = _cfg["BASE_MUT"]
1604
+ MUT_DECAY_RATE = _cfg["MUT_DECAY_RATE"]
1605
+ MUT_FLOOR = _cfg["MUT_FLOOR"]
1606
+ CROSSOVER_RATE = _cfg["CROSSOVER_RATE"]
1607
+ TARGET_FEATURES = _cfg["TARGET_FEATURES"]
1608
+ TOURNAMENT_SIZE = _cfg["TOURNAMENT_SIZE"]
1609
+ DIVERSITY_RESTART = _cfg["DIVERSITY_RESTART"]
1610
+
1611
+ N_ISLANDS = 5
1612
+ ISLAND_SIZE = POP_SIZE // N_ISLANDS
1613
+ ELITE_SIZE = max(2, POP_SIZE // 10)
1614
+ ELITE_PER_ISLAND = max(1, ISLAND_SIZE // 6)
1615
+
1616
  N_SPLITS = 3 # 3-fold walk-forward for reliable Brier estimates
1617
  GENS_PER_CYCLE = 3 # Save every 3 gens
1618
  COOLDOWN = 5 # Minimal cooldown
 
 
1619
  FAST_EVAL_GAMES = 8000 # More data in fast eval
1620
  FULL_EVAL_TOP = 10 # Full eval for top 10 (out of 60)
1621
  MIGRATION_INTERVAL = 8 # Migration every 8 gens
deploy_island.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Deploy an evolution island to any HF Space.
3
+
4
+ Usage:
5
+ source .env.local
6
+ python3 hf-space/deploy_island.py SPACE_ID ROLE [HF_TOKEN_VAR]
7
+
8
+ Examples:
9
+ python3 hf-space/deploy_island.py LBJLincoln26/nba-evo-3 extra_trees_specialist HF_TOKEN_2
10
+ python3 hf-space/deploy_island.py LBJLincoln26/nba-evo-4 catboost_specialist HF_TOKEN_2
11
+
12
+ Roles:
13
+ exploitation β€” S10 default: mut=0.09, feat=63 (proven optimal)
14
+ exploration β€” S11 default: mut=0.15, feat=80 (wider search)
15
+ extra_trees_specialist β€” extra_trees only, low mut, narrow features
16
+ catboost_specialist β€” catboost focus, medium mut
17
+ neural_specialist β€” neural models only (MLP, TabNet, FT-Transformer)
18
+ wide_search β€” high mut=0.20, feat=120, all model types
19
+ """
20
+
21
+ import os, sys
22
+ from pathlib import Path
23
+ from huggingface_hub import HfApi, CommitOperationAdd
24
+
25
+ LOCAL_DIR = Path(__file__).parent
26
+ SKIP = {"__pycache__", ".pyc", "node_modules", ".git", "deploy.py", "deploy_s11.py", "deploy_island.py"}
27
+
28
+
29
+ def main():
30
+ if len(sys.argv) < 3:
31
+ print(__doc__)
32
+ sys.exit(1)
33
+
34
+ space_id = sys.argv[1]
35
+ role = sys.argv[2]
36
+ token_var = sys.argv[3] if len(sys.argv) > 3 else "HF_TOKEN"
37
+ token = os.environ.get(token_var)
38
+
39
+ if not token:
40
+ print(f"ERROR: {token_var} not set. Run: source .env.local")
41
+ sys.exit(1)
42
+
43
+ # Engine parity check
44
+ root_engine = (LOCAL_DIR.parent / "features" / "engine.py").read_bytes()
45
+ hf_engine = (LOCAL_DIR / "features" / "engine.py").read_bytes()
46
+ if root_engine != hf_engine:
47
+ print("ERROR: features/engine.py diverged!")
48
+ sys.exit(1)
49
+ print("Engine parity: OK")
50
+
51
+ api = HfApi(token=token)
52
+ print(f"Deploying island ({role}) to {space_id}...")
53
+
54
+ operations = []
55
+ for fp in LOCAL_DIR.rglob("*"):
56
+ if fp.is_dir():
57
+ continue
58
+ if any(s in str(fp) for s in SKIP):
59
+ continue
60
+ rel = fp.relative_to(LOCAL_DIR)
61
+ print(f" + {rel}")
62
+ operations.append(CommitOperationAdd(path_in_repo=str(rel), path_or_fileobj=str(fp)))
63
+
64
+ print(f"\nUploading {len(operations)} files...")
65
+ api.create_commit(
66
+ repo_id=space_id, repo_type="space", operations=operations,
67
+ commit_message=f"feat: evolution island ({role})",
68
+ )
69
+ print("Upload OK!")
70
+
71
+ # Core secrets (DB only β€” minimal for evolution)
72
+ secrets = {
73
+ "DATABASE_URL": os.environ.get("DATABASE_URL", ""),
74
+ "SUPABASE_URL": os.environ.get("SUPABASE_URL", ""),
75
+ "SUPABASE_API_KEY": os.environ.get("SUPABASE_API_KEY", ""),
76
+ "SUPABASE_PASSWORD": os.environ.get("SUPABASE_PASSWORD", ""),
77
+ "SPACE_ROLE": role,
78
+ }
79
+
80
+ print("\nConfiguring secrets...")
81
+ for key, value in secrets.items():
82
+ if value:
83
+ try:
84
+ api.add_space_secret(space_id, key, value)
85
+ print(f" Set {key}")
86
+ except Exception as e:
87
+ print(f" WARN: {key}: {e}")
88
+
89
+ print("\nRestarting...")
90
+ try:
91
+ api.restart_space(space_id)
92
+ except Exception as e:
93
+ print(f" Restart: {e}")
94
+
95
+ owner = space_id.split("/")[0].lower()
96
+ name = space_id.split("/")[1]
97
+ print(f"\nDone! https://{owner}-{name}.hf.space")
98
+
99
+
100
+ if __name__ == "__main__":
101
+ main()
deploy_s11.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Deploy S11 as 2nd evolution island (exploration mode).
3
+
4
+ Deploys the SAME code as S10 but with SPACE_ROLE=exploration secret.
5
+ S11 uses higher mutation, wider feature search, more model diversity.
6
+
7
+ Usage:
8
+ source .env.local
9
+ python3 hf-space/deploy_s11.py
10
+ """
11
+
12
+ import os, sys
13
+ from pathlib import Path
14
+ from huggingface_hub import HfApi, CommitOperationAdd
15
+
16
+ SPACE_ID = "lbjlincoln/nomos-nba-quant-2"
17
+ HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("HF_TOKEN_2")
18
+ LOCAL_DIR = Path(__file__).parent
19
+
20
+ # Same secrets as S10 + SPACE_ROLE override
21
+ SECRETS = {
22
+ "DATABASE_URL": os.environ.get("DATABASE_URL", ""),
23
+ "SUPABASE_URL": os.environ.get("SUPABASE_URL", ""),
24
+ "SUPABASE_API_KEY": os.environ.get("SUPABASE_API_KEY", ""),
25
+ "SUPABASE_PASSWORD": os.environ.get("SUPABASE_PASSWORD", ""),
26
+ "SUPABASE_URL_2": os.environ.get("SUPABASE_URL_2", ""),
27
+ "SUPABASE_ANON_KEY_2": os.environ.get("SUPABASE_ANON_KEY_2", ""),
28
+ "SUPABASE_PASSWORD_2": os.environ.get("SUPABASE_PASSWORD_2", ""),
29
+ "SUPABASE_POOLER_2": os.environ.get("SUPABASE_POOLER_2", ""),
30
+ "ODDS_API_KEY": os.environ.get("ODDS_API_KEY", ""),
31
+ "GOOGLE_API_KEY": os.environ.get("GOOGLE_API_KEY", ""),
32
+ "HF_TOKEN": os.environ.get("HF_TOKEN", ""),
33
+ "TELEGRAM_BOT_TOKEN": os.environ.get("TELEGRAM_BOT_TOKEN", ""),
34
+ "ADMIN_TELEGRAM_ID": os.environ.get("ADMIN_TELEGRAM_ID", ""),
35
+ # S11 differentiator: exploration mode
36
+ "SPACE_ROLE": "exploration",
37
+ }
38
+
39
+ # Files to skip (S11 doesn't need deploy scripts or experiment_runner)
40
+ SKIP = {"__pycache__", ".pyc", "node_modules", ".git", "deploy.py", "deploy_s11.py"}
41
+
42
+
43
+ def main():
44
+ if not HF_TOKEN:
45
+ print("ERROR: HF_TOKEN not set. Run: source .env.local")
46
+ sys.exit(1)
47
+
48
+ # Engine parity check
49
+ root_engine = (LOCAL_DIR.parent / "features" / "engine.py").read_bytes()
50
+ hf_engine = (LOCAL_DIR / "features" / "engine.py").read_bytes()
51
+ if root_engine != hf_engine:
52
+ print("ERROR: features/engine.py diverged! Fix parity first.")
53
+ sys.exit(1)
54
+ print("Engine parity check: OK")
55
+
56
+ api = HfApi(token=HF_TOKEN)
57
+ print(f"Deploying S11 (EXPLORATION island) to {SPACE_ID}...")
58
+
59
+ operations = []
60
+ for fp in LOCAL_DIR.rglob("*"):
61
+ if fp.is_dir():
62
+ continue
63
+ if any(s in str(fp) for s in SKIP):
64
+ continue
65
+ rel = fp.relative_to(LOCAL_DIR)
66
+ print(f" + {rel}")
67
+ operations.append(CommitOperationAdd(path_in_repo=str(rel), path_or_fileobj=str(fp)))
68
+
69
+ if not operations:
70
+ print("ERROR: No files found")
71
+ sys.exit(1)
72
+
73
+ print(f"\nUploading {len(operations)} files...")
74
+ try:
75
+ api.create_commit(
76
+ repo_id=SPACE_ID, repo_type="space", operations=operations,
77
+ commit_message="feat: S11 as exploration island (higher mut, wider features)",
78
+ )
79
+ print("Upload OK!")
80
+ except Exception as e:
81
+ if "404" in str(e) or "not found" in str(e).lower():
82
+ print(f"Space not found, creating {SPACE_ID}...")
83
+ api.create_repo(repo_id=SPACE_ID, repo_type="space", space_sdk="docker",
84
+ space_hardware="cpu-basic", private=False)
85
+ api.create_commit(
86
+ repo_id=SPACE_ID, repo_type="space", operations=operations,
87
+ commit_message="feat: S11 as exploration island (higher mut, wider features)",
88
+ )
89
+ else:
90
+ raise
91
+
92
+ print("\nConfiguring secrets (including SPACE_ROLE=exploration)...")
93
+ for key, value in SECRETS.items():
94
+ if value:
95
+ try:
96
+ api.add_space_secret(SPACE_ID, key, value)
97
+ print(f" Set {key}")
98
+ except Exception as e:
99
+ print(f" WARN: {key}: {e}")
100
+
101
+ print("\nRestarting Space...")
102
+ try:
103
+ api.restart_space(SPACE_ID)
104
+ except Exception as e:
105
+ print(f" Restart: {e}")
106
+
107
+ print(f"\nDone! S11 (exploration): https://lbjlincoln-nomos-nba-quant-2.hf.space")
108
+ print(f"Monitor: https://huggingface.co/spaces/{SPACE_ID}")
109
+ print("\nS11 config: SPACE_ROLE=exploration β†’ mut=0.15, cx=0.70, feat=80, tournament=5")
110
+
111
+
112
+ if __name__ == "__main__":
113
+ main()