Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b52ba4660 | ||
|
|
9a783ec8aa |
@@ -175,6 +175,13 @@ def run_evolution(
|
|||||||
# строки в базу идут только когда остров улучшился — иначе элитизм
|
# строки в базу идут только когда остров улучшился — иначе элитизм
|
||||||
# дублировал бы один и тот же геном в топ дашборда каждое поколение
|
# дублировал бы один и тот же геном в топ дашборда каждое поколение
|
||||||
written_best_fitness = [float("-inf")] * islands
|
written_best_fitness = [float("-inf")] * islands
|
||||||
|
# СКОРОСТЬ отслеживается отдельно: у дашборда два топа (КПД и
|
||||||
|
# скорость), а рекордсмены скорости — почти всегда «прожорливые»
|
||||||
|
# геномы с низким КПД, которые КПД-рекорд не улучшают. Без этого
|
||||||
|
# трекера топ скоростей замирал навсегда (реальный кейс: после
|
||||||
|
# перехода на выборочную запись он не обновлялся сутки, пока
|
||||||
|
# эволюция находила всё новые быстрые геномы)
|
||||||
|
written_best_velocity = [float("-inf")] * islands
|
||||||
|
|
||||||
for generation in range(n_generations):
|
for generation in range(n_generations):
|
||||||
# все острова — ОДНИМ плоским батчем (жирный батч кормит GPU)
|
# все острова — ОДНИМ плоским батчем (жирный батч кормит GPU)
|
||||||
@@ -209,6 +216,17 @@ def run_evolution(
|
|||||||
written_best_fitness[i] = pairs[0][1].fitness
|
written_best_fitness[i] = pairs[0][1].fitness
|
||||||
queue.put(_best_record(pairs[0][0], pairs[0][1], db, bounds, mode))
|
queue.put(_best_record(pairs[0][0], pairs[0][1], db, bounds, mode))
|
||||||
recorded.add(id(pairs[0][1]))
|
recorded.add(id(pairs[0][1]))
|
||||||
|
# рекорд СКОРОСТИ острова — тоже полной строкой
|
||||||
|
fastest = max(
|
||||||
|
(p for p in pairs if p[1].feasible and p[1].exit_velocity_mps is not None),
|
||||||
|
key=lambda p: p[1].exit_velocity_mps,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
if fastest is not None and fastest[1].exit_velocity_mps > written_best_velocity[i]:
|
||||||
|
written_best_velocity[i] = fastest[1].exit_velocity_mps
|
||||||
|
if id(fastest[1]) not in recorded:
|
||||||
|
queue.put(_best_record(fastest[0], fastest[1], db, bounds, mode))
|
||||||
|
recorded.add(id(fastest[1]))
|
||||||
next_population = [copy.deepcopy(pair[0]) for pair in pairs[:elitism]]
|
next_population = [copy.deepcopy(pair[0]) for pair in pairs[:elitism]]
|
||||||
while len(next_population) < population_size:
|
while len(next_population) < population_size:
|
||||||
parent_a = _tournament_select(pairs, rng)
|
parent_a = _tournament_select(pairs, rng)
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ PAGE_HTML = r"""<!doctype html>
|
|||||||
<button class="tgl" id="tab-vel" onclick="setRank('vel')">по скорости</button>
|
<button class="tgl" id="tab-vel" onclick="setRank('vel')">по скорости</button>
|
||||||
</h2>
|
</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead><tr><th>КПД</th><th>Скор., м/с</th><th>Ступ.</th><th>Цена, ₽</th><th>Режим</th></tr></thead>
|
<thead><tr><th>КПД</th><th>Скор., м/с</th><th>Ступ.</th><th>Цена, ₽</th><th>Режим</th><th>Когда найден</th></tr></thead>
|
||||||
<tbody id="top"></tbody>
|
<tbody id="top"></tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="muted" style="font-size:12px">Клик по строке — полная детализация прогона (расстояния, номиналы, физика).</p>
|
<p class="muted" style="font-size:12px">Клик по строке — полная детализация прогона (расстояния, номиналы, физика).</p>
|
||||||
@@ -109,13 +109,23 @@ function setRank(m){ rankMode=m; if(lastOverview) renderTop(lastOverview);
|
|||||||
document.getElementById('tab-eff').classList.toggle('active', m==='eff');
|
document.getElementById('tab-eff').classList.toggle('active', m==='eff');
|
||||||
document.getElementById('tab-vel').classList.toggle('active', m==='vel'); }
|
document.getElementById('tab-vel').classList.toggle('active', m==='vel'); }
|
||||||
|
|
||||||
|
// «Когда найден»: без этой колонки обновление топа было НЕВИДИМО — новая
|
||||||
|
// строка 13.63% глазом неотличима от старой 13.44%, и казалось, что таблица
|
||||||
|
// застыла. Свежие записи (< 1 часа) подсвечиваются.
|
||||||
|
const fmtWhen = ts => {
|
||||||
|
if(!ts) return "—";
|
||||||
|
const d = new Date(ts), ageMs = Date.now()-d.getTime();
|
||||||
|
const t = d.toLocaleString([], {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'});
|
||||||
|
return ageMs < 3600e3 ? `<b style="color:#3fb950">${t}</b>` : t;
|
||||||
|
};
|
||||||
function renderTop(o){
|
function renderTop(o){
|
||||||
const rows = rankMode==='vel' ? (o.top_by_velocity||[]) : (o.top||[]);
|
const rows = rankMode==='vel' ? (o.top_by_velocity||[]) : (o.top||[]);
|
||||||
const top = document.getElementById('top'); top.innerHTML='';
|
const top = document.getElementById('top'); top.innerHTML='';
|
||||||
rows.forEach(t=>{
|
rows.forEach(t=>{
|
||||||
const tr=document.createElement('tr'); tr.className='clk';
|
const tr=document.createElement('tr'); tr.className='clk';
|
||||||
tr.innerHTML=`<td>${fmtPct(t.efficiency)}</td><td>${fmt(t.exit_velocity_mps,1)}</td>`+
|
tr.innerHTML=`<td>${fmtPct(t.efficiency)}</td><td>${fmt(t.exit_velocity_mps,1)}</td>`+
|
||||||
`<td>${t.n_stages??'—'}</td><td>${fmt(t.cost_rub,0)}</td><td><span class="pill">${t.search_mode}</span></td>`;
|
`<td>${t.n_stages??'—'}</td><td>${fmt(t.cost_rub,0)}</td><td><span class="pill">${t.search_mode}</span></td>`+
|
||||||
|
`<td class="muted" style="white-space:nowrap">${fmtWhen(t.timestamp)}</td>`;
|
||||||
tr.onclick=()=>showDetail(t.run_id);
|
tr.onclick=()=>showDetail(t.run_id);
|
||||||
top.appendChild(tr);
|
top.appendChild(tr);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ def test_run_evolution_counts_all_evaluations_but_stores_only_best_rows(tmp_path
|
|||||||
conn = open_connection(db_path)
|
conn = open_connection(db_path)
|
||||||
assert _stats_total(conn) == 12 # счётчики дашборда видят все оценки
|
assert _stats_total(conn) == 12 # счётчики дашборда видят все оценки
|
||||||
n_rows = count_runs(conn)
|
n_rows = count_runs(conn)
|
||||||
# строк меньше, чем оценок: максимум 1 улучшение на остров на поколение
|
# строк меньше, чем оценок: максимум 2 улучшения (КПД+скорость) на
|
||||||
assert 1 <= n_rows <= 2
|
# остров на поколение
|
||||||
|
assert 1 <= n_rows <= 4
|
||||||
rows = fetch_runs(conn)
|
rows = fetch_runs(conn)
|
||||||
assert all(r.search_mode == "evolve" for r in rows)
|
assert all(r.search_mode == "evolve" for r in rows)
|
||||||
# лучший найденный обязан лежать в базе полной строкой
|
# лучший найденный обязан лежать в базе полной строкой
|
||||||
@@ -91,7 +92,8 @@ def test_run_evolution_gpu_batch_mode(tmp_path):
|
|||||||
assert summary["n_evaluated"] == 120
|
assert summary["n_evaluated"] == 120
|
||||||
conn = open_connection(db_path)
|
conn = open_connection(db_path)
|
||||||
assert _stats_total(conn) == 120
|
assert _stats_total(conn) == 120
|
||||||
assert 1 <= count_runs(conn) <= 3 # 1 остров x 3 поколения, только улучшения
|
# 1 остров x 3 поколения, максимум 2 улучшения (КПД+скорость) за поколение
|
||||||
|
assert 1 <= count_runs(conn) <= 6
|
||||||
rows = fetch_runs(conn, limit=5)
|
rows = fetch_runs(conn, limit=5)
|
||||||
assert all(r.search_mode.startswith("evolve-gpu") for r in rows)
|
assert all(r.search_mode.startswith("evolve-gpu") for r in rows)
|
||||||
# у сохранённых лучших должна быть посчитана стоимость (GPU-путь
|
# у сохранённых лучших должна быть посчитана стоимость (GPU-путь
|
||||||
@@ -111,5 +113,5 @@ def test_run_evolution_islands(tmp_path):
|
|||||||
assert summary["n_evaluated"] == 4 * 15 * 3
|
assert summary["n_evaluated"] == 4 * 15 * 3
|
||||||
conn = open_connection(db_path)
|
conn = open_connection(db_path)
|
||||||
assert _stats_total(conn) == 4 * 15 * 3
|
assert _stats_total(conn) == 4 * 15 * 3
|
||||||
# максимум одно улучшение на остров на поколение
|
# максимум 2 улучшения (КПД+скорость) на остров на поколение
|
||||||
assert 3 <= count_runs(conn) <= 4 * 3
|
assert 3 <= count_runs(conn) <= 4 * 3 * 2
|
||||||
|
|||||||
Reference in New Issue
Block a user