/
elgraf
/
nir_2D_BPP
Обзор
Документация
Войти
/
elgraf
/
nir_2D_BPP
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scripts/quick_artifacts.py
190 строк
9 KB
e1graf
initial commit
12 май 2026, 17:16
12 май 2026, 17:16
aaa59ac
Код
Авторство
О чём код?
"""Быстрый отбор отчётных артефактов: 6 экземпляров, каждый — однократный прогон GA с лучшим seed из CSV. Стратегия: * Из results/class_representative_runs.csv выбираем 4 экземпляра, на которых GA даёт максимальное улучшение по числу листов относительно FFD, с разными классами и размерностями. * Из results/synthetic_runs.csv выбираем 2 экземпляра, на которых GA улучшает FFD (например, trap_for_ffd и rotation_critical). * Для каждого: загружаем seed лучшего GA-запуска из CSV, повторяем GA с тем же seed, строим PNG раскладок (FFD и GA) и график сходимости. * Пишем selected_artifacts.csv и README.md в results/convergence/report_selected/. Это занимает порядка 10-15 минут (вместо ~1 часа в полной _select_category_artifacts). """ from __future__ import annotations import os import shutil import sys from dataclasses import replace from pathlib import Path import pandas as pd PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT / "src")) from cutting_ga.baselines import ffd_blf from cutting_ga.console import configure_utf8_output from cutting_ga.decoders.blf import BLFDecoder from cutting_ga.ga.algorithm import GAConfig, GeneticAlgorithm from cutting_ga.parser import load_instance from cutting_ga.visualization import plot_convergence, plot_solution def best_per_instance(df: pd.DataFrame, method: str = "ga_blf") -> pd.DataFrame: """Best (по числу листов, потом по КИМ) row per (instance) среди заданного метода.""" sub = df[df["method"] == method] return ( sub.sort_values(["sheet_count", "material_utilization"], ascending=[True, False]) .drop_duplicates(subset=["instance"], keep="first") ) def pick_candidates() -> list[tuple[str, str]]: """Вернуть список (instance, category).""" rep = pd.read_csv("results/class_representative_runs.csv") syn = pd.read_csv("results/synthetic_runs.csv") rep_ffd = best_per_instance(rep, "ffd_blf") rep_ga = best_per_instance(rep, "ga_blf") rep_pivot = rep_ffd[["instance", "sheet_count", "instance_class", "n_items"]].rename( columns={"sheet_count": "ffd_sheets"} ).merge( rep_ga[["instance", "sheet_count", "seed"]].rename(columns={"sheet_count": "ga_sheets"}), on="instance", how="left", ) rep_pivot["improvement"] = rep_pivot["ffd_sheets"] - rep_pivot["ga_sheets"] rep_pivot = rep_pivot[rep_pivot["improvement"] > 0] # Выбираем по 1 экземпляру из четырёх разных классов с максимальным improvement. chosen = [] seen_classes: set[str] = set() for _, row in rep_pivot.sort_values(["improvement", "n_items"], ascending=[False, False]).iterrows(): if row["instance_class"] in seen_classes: continue chosen.append(row["instance"]) seen_classes.add(row["instance_class"]) if len(chosen) >= 4: break syn_ffd = best_per_instance(syn, "ffd_blf") syn_ga = best_per_instance(syn, "ga_blf") syn_pivot = syn_ffd[["instance", "sheet_count"]].rename(columns={"sheet_count": "ffd_sheets"}).merge( syn_ga[["instance", "sheet_count"]].rename(columns={"sheet_count": "ga_sheets"}), on="instance", ) syn_pivot["improvement"] = syn_pivot["ffd_sheets"] - syn_pivot["ga_sheets"] syn_pivot = syn_pivot[syn_pivot["improvement"] > 0] syn_chosen: list[str] = [] for _, row in syn_pivot.sort_values(["improvement"], ascending=False).iterrows(): # один rotation_critical + один trap_for_ffd stem = Path(row["instance"]).stem kind = "rotation_critical" if "rotation_critical" in stem else "trap_for_ffd" if "trap_for_ffd" in stem else "other" if kind in [Path(p).stem.split("_0")[0] for p in syn_chosen]: continue syn_chosen.append(row["instance"]) if len(syn_chosen) >= 2: break return [(p, "representative") for p in chosen] + [(p, "synthetic") for p in syn_chosen] def render_one(instance: str, category: str, output_conv: Path, output_layouts: Path) -> dict: """Перерасчитать один экземпляр с тем же seed, что и лучший в CSV, и сохранить PNG.""" csv_path = { "representative": "results/class_representative_runs.csv", "synthetic": "results/synthetic_runs.csv", }[category] df = pd.read_csv(csv_path) best_row = ( df[(df["method"] == "ga_blf") & (df["instance"] == instance)] .sort_values(["sheet_count", "material_utilization"], ascending=[True, False]) .iloc[0] ) seed = int(best_row["seed"]) cfg = replace(GAConfig(), seed=seed, population_size=100, max_generations=200, stagnation_limit=40) sheet, items = load_instance(instance) ffd_solution = ffd_blf(items, sheet) ga_result = GeneticAlgorithm(BLFDecoder(), cfg).run(items, sheet) stem = Path(instance).stem conv_png = output_conv / f"{stem}_ga_blf_convergence.png" layout_ga = output_layouts / f"{stem}_ga_blf_layout.png" layout_ffd = output_layouts / f"{stem}_ffd_blf_layout.png" plot_convergence(ga_result.history, conv_png, l1=ga_result.best_solution.lower_bound_l1, stopped_by=ga_result.stopped_by) plot_solution(ga_result.best_solution, layout_ga, instance_name=stem, method_name="ga_blf") plot_solution(ffd_solution, layout_ffd, instance_name=stem, method_name="ffd_blf") return { "instance": instance, "category": category, "n_items": len(items), "seed": seed, "l1": ga_result.best_solution.lower_bound_l1, "ffd_sheet_count": ffd_solution.sheet_count, "ga_final_sheet_count": ga_result.best_solution.sheet_count, "ga_initial_sheet_count": int(ga_result.history[0]["best_sheet_count"]), "ffd_kim": ffd_solution.material_utilization, "ga_kim": ga_result.best_solution.material_utilization, "generations_run": ga_result.generations_run, "stopped_by": ga_result.stopped_by, "convergence_png": str(conv_png), "ga_layout_png": str(layout_ga), "ffd_layout_png": str(layout_ffd), "why": f"GA лучше FFD на {ffd_solution.sheet_count - ga_result.best_solution.sheet_count} лист(ов)", } def main() -> None: os.chdir(PROJECT_ROOT) configure_utf8_output() output_conv = Path("results/convergence/report_selected") output_layouts = Path("results/layouts/report_selected") if output_conv.exists(): shutil.rmtree(output_conv) if output_layouts.exists(): shutil.rmtree(output_layouts) output_conv.mkdir(parents=True, exist_ok=True) output_layouts.mkdir(parents=True, exist_ok=True) chosen = pick_candidates() print(f"выбрано {len(chosen)} экземпляров:") for inst, cat in chosen: print(f" - [{cat}] {inst}") rendered: list[dict] = [] for idx, (instance, category) in enumerate(chosen, start=1): print(f"[{idx}/{len(chosen)}] {category}: {instance}") rendered.append(render_one(instance, category, output_conv, output_layouts)) pd.DataFrame(rendered).to_csv(output_conv / "selected_artifacts.csv", index=False) lines = ["# Выбранные артефакты для отчёта", ""] for row in rendered: stem = Path(row["instance"]).stem lines.extend( [ f"## {stem}", f"- категория: {row['category']}", f"- пример: {row['instance']}", f"- деталей: {row['n_items']}", f"- L1: {row['l1']}", f"- листов FFD: {row['ffd_sheet_count']}", f"- листов GA: {row['ga_final_sheet_count']}", f"- КИМ FFD: {row['ffd_kim']:.4f}", f"- КИМ GA: {row['ga_kim']:.4f}", f"- seed лучшего прогона: {row['seed']}", f"- поколений выполнено: {row['generations_run']}", f"- причина остановки: {row['stopped_by']}", f"- PNG сходимости: {row['convergence_png']}", f"- PNG GA: {row['ga_layout_png']}", f"- PNG FFD: {row['ffd_layout_png']}", f"- комментарий: {row['why']}", "", ] ) (output_conv / "README.md").write_text("\n".join(lines), encoding="utf-8") print("готово") if __name__ == "__main__": main()