TwoQuarks commited on
Commit
612a416
·
verified ·
1 Parent(s): b442b8c

Update Down/plot_dual_down_antidown.py

Browse files
Files changed (1) hide show
  1. Down/plot_dual_down_antidown.py +91 -40
Down/plot_dual_down_antidown.py CHANGED
@@ -1,10 +1,8 @@
1
  """
2
  Overlay plot: Down vs AntiDown (tabular experiments).
3
 
4
- Reads CSVs from TWOQUARKS_RESULTS_DIR (or ./results) and writes PNG into
5
- TWOQUARKS_GRAPHICS_DIR (or ./graphics).
6
-
7
- This is intentionally simple and dependency-light (pandas optional).
8
  """
9
 
10
  from __future__ import annotations
@@ -13,6 +11,7 @@ from __future__ import annotations
13
  import matplotlib
14
  matplotlib.use("Agg")
15
 
 
16
  import csv
17
  import os
18
  from collections import defaultdict
@@ -22,6 +21,10 @@ from typing import Dict, List, Tuple
22
  import matplotlib.pyplot as plt
23
 
24
 
 
 
 
 
25
  def _read_csv(path: Path) -> List[Dict[str, str]]:
26
  with path.open("r", newline="") as f:
27
  return list(csv.DictReader(f))
@@ -33,35 +36,34 @@ def _mean(xs: List[float]) -> float:
33
  return sum(xs) / len(xs)
34
 
35
 
 
 
 
 
36
  def _down_series(rows: List[Dict[str, str]]) -> Tuple[List[int], List[float]]:
37
  """
38
  Down CSV fields (expected):
39
  phase, episode, agent, episode_reward, [global_episode], ...
40
- We aggregate by (phase, episode) averaging across agents.
41
  """
42
  bucket: Dict[Tuple[int, int], List[float]] = defaultdict(list)
 
43
  for r in rows:
44
  try:
45
- phase = int(r.get("phase", "0"))
46
- ep = int(r.get("episode", "0"))
47
- rew = float(r.get("episode_reward", "nan"))
48
  except Exception:
49
  continue
50
  bucket[(phase, ep)].append(rew)
51
 
52
- # Sort by phase then episode, make a single x-axis that is "global episode"
53
- keys = sorted(bucket.keys())
54
  xs, ys = [], []
55
  g = 0
56
- last_phase = None
57
- for (phase, ep) in keys:
58
- if last_phase is None:
59
- last_phase = phase
60
- if phase != last_phase:
61
- last_phase = phase
62
  xs.append(g)
63
- ys.append(_mean(bucket[(phase, ep)]))
64
  g += 1
 
65
  return xs, ys
66
 
67
 
@@ -69,55 +71,101 @@ def _antidown_series(rows: List[Dict[str, str]]) -> Tuple[List[int], List[float]
69
  """
70
  AntiDown CSV fields (expected):
71
  phase, episode, agent, total_reward, ...
72
- We aggregate by (phase, episode) averaging across agents.
73
  """
74
  bucket: Dict[Tuple[int, int], List[float]] = defaultdict(list)
 
75
  for r in rows:
76
  try:
77
- phase = int(r.get("phase", "0"))
78
- ep = int(r.get("episode", "0"))
79
- rew = float(r.get("total_reward", "nan"))
80
  except Exception:
81
  continue
82
  bucket[(phase, ep)].append(rew)
83
 
84
- keys = sorted(bucket.keys())
85
  xs, ys = [], []
86
  g = 0
87
- last_phase = None
88
- for (phase, ep) in keys:
89
- if last_phase is None:
90
- last_phase = phase
91
- if phase != last_phase:
92
- last_phase = phase
93
  xs.append(g)
94
- ys.append(_mean(bucket[(phase, ep)]))
95
  g += 1
 
96
  return xs, ys
97
 
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  def main() -> None:
 
 
100
  root = Path(__file__).resolve().parent
101
- results_dir = Path(os.environ.get("TWOQUARKS_RESULTS_DIR", (root / "results").as_posix()))
102
- graphics_dir = Path(os.environ.get("TWOQUARKS_GRAPHICS_DIR", (root / "graphics").as_posix()))
 
 
 
 
 
 
 
 
103
  graphics_dir.mkdir(parents=True, exist_ok=True)
104
 
105
  down_csv = results_dir / "down_paradox_tabular_results.csv"
106
  anti_csv = results_dir / "antidown_corrupted_valley_tabular.csv"
107
 
108
- if not down_csv.exists() or not anti_csv.exists():
109
- print(f"[dual] missing CSVs. down={down_csv.exists()} anti={anti_csv.exists()}. Skipping.")
110
- return
 
111
 
112
  down_rows = _read_csv(down_csv)
113
  anti_rows = _read_csv(anti_csv)
114
- if not down_rows or not anti_rows:
115
- print("[dual] one CSV is empty. Skipping.")
116
- return
 
 
117
 
118
  x1, y1 = _down_series(down_rows)
119
  x2, y2 = _antidown_series(anti_rows)
120
 
 
 
 
 
 
 
121
  plt.figure(figsize=(10, 5))
122
  plt.plot(x1, y1, label="Down (mean across agents)")
123
  plt.plot(x2, y2, label="AntiDown (mean across agents)")
@@ -125,10 +173,13 @@ def main() -> None:
125
  plt.ylabel("Episode return")
126
  plt.title("Down vs AntiDown — Tabular returns (mean across agents)")
127
  plt.legend()
128
- out = graphics_dir / "dual_down_antidown_episode_return.png"
129
  plt.tight_layout()
130
- plt.savefig(out, dpi=160)
131
- print(f"[dual] saved {out}")
 
 
 
 
132
 
133
 
134
  if __name__ == "__main__":
 
1
  """
2
  Overlay plot: Down vs AntiDown (tabular experiments).
3
 
4
+ Reads CSVs from a shared results directory and writes a PNG overlay plot.
5
+ This is the canonical comparative result for the Down vs AntiDown experiment.
 
 
6
  """
7
 
8
  from __future__ import annotations
 
11
  import matplotlib
12
  matplotlib.use("Agg")
13
 
14
+ import argparse
15
  import csv
16
  import os
17
  from collections import defaultdict
 
21
  import matplotlib.pyplot as plt
22
 
23
 
24
+ # ----------------------------
25
+ # Utilities
26
+ # ----------------------------
27
+
28
  def _read_csv(path: Path) -> List[Dict[str, str]]:
29
  with path.open("r", newline="") as f:
30
  return list(csv.DictReader(f))
 
36
  return sum(xs) / len(xs)
37
 
38
 
39
+ # ----------------------------
40
+ # Series builders
41
+ # ----------------------------
42
+
43
  def _down_series(rows: List[Dict[str, str]]) -> Tuple[List[int], List[float]]:
44
  """
45
  Down CSV fields (expected):
46
  phase, episode, agent, episode_reward, [global_episode], ...
47
+ Aggregate by (phase, episode), averaging across agents.
48
  """
49
  bucket: Dict[Tuple[int, int], List[float]] = defaultdict(list)
50
+
51
  for r in rows:
52
  try:
53
+ phase = int(r["phase"])
54
+ ep = int(r["episode"])
55
+ rew = float(r["episode_reward"])
56
  except Exception:
57
  continue
58
  bucket[(phase, ep)].append(rew)
59
 
 
 
60
  xs, ys = [], []
61
  g = 0
62
+ for key in sorted(bucket.keys()):
 
 
 
 
 
63
  xs.append(g)
64
+ ys.append(_mean(bucket[key]))
65
  g += 1
66
+
67
  return xs, ys
68
 
69
 
 
71
  """
72
  AntiDown CSV fields (expected):
73
  phase, episode, agent, total_reward, ...
74
+ Aggregate by (phase, episode), averaging across agents.
75
  """
76
  bucket: Dict[Tuple[int, int], List[float]] = defaultdict(list)
77
+
78
  for r in rows:
79
  try:
80
+ phase = int(r["phase"])
81
+ ep = int(r["episode"])
82
+ rew = float(r["total_reward"])
83
  except Exception:
84
  continue
85
  bucket[(phase, ep)].append(rew)
86
 
 
87
  xs, ys = [], []
88
  g = 0
89
+ for key in sorted(bucket.keys()):
 
 
 
 
 
90
  xs.append(g)
91
+ ys.append(_mean(bucket[key]))
92
  g += 1
93
+
94
  return xs, ys
95
 
96
 
97
+ # ----------------------------
98
+ # CLI
99
+ # ----------------------------
100
+
101
+ def _build_parser() -> argparse.ArgumentParser:
102
+ p = argparse.ArgumentParser(description="Plot Down vs AntiDown (dual overlay).")
103
+ p.add_argument(
104
+ "--results_dir",
105
+ type=str,
106
+ default="",
107
+ help="Directory containing both CSVs. Defaults to TWOQUARKS_RESULTS_DIR or ./results.",
108
+ )
109
+ p.add_argument(
110
+ "--out",
111
+ type=str,
112
+ default="",
113
+ help="Output directory for PNG. Defaults to TWOQUARKS_GRAPHICS_DIR or ./graphics.",
114
+ )
115
+ p.add_argument(
116
+ "--prefix",
117
+ type=str,
118
+ default="dual_down_antidown",
119
+ help="Filename prefix for output image.",
120
+ )
121
+ return p
122
+
123
+
124
+ # ----------------------------
125
+ # Main
126
+ # ----------------------------
127
+
128
  def main() -> None:
129
+ args = _build_parser().parse_args()
130
+
131
  root = Path(__file__).resolve().parent
132
+
133
+ results_dir = Path(
134
+ args.results_dir
135
+ or os.environ.get("TWOQUARKS_RESULTS_DIR", (root / "results").as_posix())
136
+ )
137
+
138
+ graphics_dir = Path(
139
+ args.out
140
+ or os.environ.get("TWOQUARKS_GRAPHICS_DIR", (root / "graphics").as_posix())
141
+ )
142
  graphics_dir.mkdir(parents=True, exist_ok=True)
143
 
144
  down_csv = results_dir / "down_paradox_tabular_results.csv"
145
  anti_csv = results_dir / "antidown_corrupted_valley_tabular.csv"
146
 
147
+ if not down_csv.exists():
148
+ raise FileNotFoundError(f"Missing Down CSV: {down_csv}")
149
+ if not anti_csv.exists():
150
+ raise FileNotFoundError(f"Missing AntiDown CSV: {anti_csv}")
151
 
152
  down_rows = _read_csv(down_csv)
153
  anti_rows = _read_csv(anti_csv)
154
+
155
+ if not down_rows:
156
+ raise RuntimeError("Down CSV is empty.")
157
+ if not anti_rows:
158
+ raise RuntimeError("AntiDown CSV is empty.")
159
 
160
  x1, y1 = _down_series(down_rows)
161
  x2, y2 = _antidown_series(anti_rows)
162
 
163
+ if len(x1) != len(x2):
164
+ print(
165
+ f"[dual] WARNING: series length mismatch "
166
+ f"(Down={len(x1)}, AntiDown={len(x2)})"
167
+ )
168
+
169
  plt.figure(figsize=(10, 5))
170
  plt.plot(x1, y1, label="Down (mean across agents)")
171
  plt.plot(x2, y2, label="AntiDown (mean across agents)")
 
173
  plt.ylabel("Episode return")
174
  plt.title("Down vs AntiDown — Tabular returns (mean across agents)")
175
  plt.legend()
 
176
  plt.tight_layout()
177
+
178
+ out_path = graphics_dir / f"{args.prefix}_episode_return.png"
179
+ plt.savefig(out_path, dpi=160)
180
+ plt.close()
181
+
182
+ print(f"[dual] saved {out_path}")
183
 
184
 
185
  if __name__ == "__main__":