Skip to content

plotting cleanup#30

Merged
rbx merged 3 commits intomasterfrom
plotter-cleanup
Mar 9, 2026
Merged

plotting cleanup#30
rbx merged 3 commits intomasterfrom
plotter-cleanup

Conversation

@rbx
Copy link
Member

@rbx rbx commented Mar 9, 2026

  • remove old plot_rewards
  • remove legacy cumulative savings plotter
  • rename plot() to plot_episode and move it to plotter.py
  • delete the now empty plot.py

also:

  • remove carry_over_state option - do it always unconditionally instead

- remove old plot_rewards
- remove legacy cumulative savings plotter
- rename plot() to plot_episode and move it to plotter.py
- delete the now empty plot.py
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 99c38838-de3f-44f8-b9fd-a5eb78143975

📥 Commits

Reviewing files that changed from the base of the PR and between 45157b6 and e8f3957.

📒 Files selected for processing (6)
  • .github/workflows/tests.yml
  • analyze_lambda_occupancy.py
  • src/environment.py
  • test/test_sanity_env.py
  • train.py
  • train_iter.py
💤 Files with no reviewable changes (1)
  • analyze_lambda_occupancy.py

📝 Walkthrough

Walkthrough

Removes legacy plotting module and flags, introduces per-episode plotting in src/plotter.py (plot_episode), deletes plot_rewards config and carry_over_state CLI/constructor usage across env and orchestration code, updates tests to new plotting API, and adds a CI step to run a workload-generator inspection test.

Changes

Cohort / File(s) Summary
Plot removal & replacement
src/plot.py, src/plotter.py
Deletes src/plot.py and its functions (plot, plot_reward, plot_cumulative_savings); adds plot_episode(...) in src/plotter.py and adjusts dashboard filename/save naming logic.
Plot configuration
src/plot_config.py
Removes plot_rewards: bool = False from PlotConfig.
Environment integration
src/environment.py
Replaces imports from src.plot with from src.plotter import plot_episode; removes carry_over_state constructor parameter and related branching; calls plot_episode(...) at episode boundaries.
Training / CLI orchestration
train.py, train_iter.py, analyze_lambda_occupancy.py
Removes --plot-rewards and --carry-over-state flags and all emission/forwarding of carry_over_state; updates evaluation plotting calls to use consolidated plotter APIs and passes workload_gen where applicable.
Tests
test/test_plotter.py, test/test_sanity_env.py, test/...
Updates tests to import/use plot_episode; renames TestPlotSimple → TestPlotEpisode; removes cumulative-savings legacy test and carry-over-state test branching; drops gym check CLI path.
CI workflow
.github/workflows/tests.yml
Removes --check-gym from a sanity step, removes an extra external-data sanity step, and adds a step "Run workload generator inspection test" executing python -m test.test_inspect_workloadgen with Poisson workload parameters and plotting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'plotting cleanup' accurately summarizes the main change: removal of old plotting functions and consolidation into a new plot_episode function in plotter.py, along with the removal of the now-empty plot.py file.
Description check ✅ Passed The description is directly related to the changeset, covering the removal of plot_rewards, legacy cumulative savings plotter, renaming plot() to plot_episode, deletion of plot.py, and removal of carry_over_state option.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/test_plotter.py (1)

295-306: ⚠️ Potential issue | 🔴 Critical

output_dir fixture is undefined and tests will fail.

The test_saves_file and test_with_plot_flags_disabled methods expect an output_dir pytest fixture that does not exist in the repository. These tests will fail when run with pytest. The test_no_save method demonstrates the available pattern: use tempfile.TemporaryDirectory() inline instead of relying on a fixture.

Either define the output_dir fixture in test/conftest.py or refactor these methods to use inline tempfile.TemporaryDirectory() for consistency with test_no_save.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/test_plotter.py` around lines 295 - 306, Tests reference a non-existent
pytest fixture output_dir causing failures; update
TestPlotEpisode.test_saves_file (and test_with_plot_flags_disabled if present)
to use tempfile.TemporaryDirectory() inline like test_no_save: import tempfile,
create a TemporaryDirectory() context, set env.plots_dir to f"{tmpdir}/"
(ensuring trailing slash), call plot_episode(env, ... , save=True, show=False,
suffix=1) and then inspect files in that temporary directory; alternatively add
a simple output_dir fixture to conftest.py that yields a TemporaryDirectory
path, but prefer the inline TemporaryDirectory approach for consistency with
test_no_save and to keep tests self-contained.
🧹 Nitpick comments (1)
src/plotter.py (1)

172-175: Minor inconsistency: Use os.path.join() for path construction.

plot_dashboard (line 329) and plot_cumulative_savings (line 414) use os.path.join() for path construction, but plot_episode uses direct string concatenation. This assumes plots_dir has a trailing slash.

Suggested fix for consistency
     if save:
         timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
-        plt.savefig(f"{env.plots_dir}{prefix}_{suffix:09d}_{timestamp}.png")
-        print(f"Figure saved as: {env.plots_dir}{prefix}_{suffix:09d}_{timestamp}.png\nExpecting next save after {env.next_plot_save + env.steps_per_iteration}")
+        fname = f"{prefix}_{suffix:09d}_{timestamp}.png"
+        save_path = os.path.join(env.plots_dir, fname)
+        plt.savefig(save_path)
+        print(f"Figure saved as: {save_path}\nExpecting next save after {env.next_plot_save + env.steps_per_iteration}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plotter.py` around lines 172 - 175, The save path in the plot_episode
save block uses string concatenation and assumes a trailing slash; change the
code in the save branch (the plt.savefig and subsequent print) to build the
filename with os.path.join(env.plots_dir,
f"{prefix}_{suffix:09d}_{timestamp}.png") and reuse that joined path for both
plt.savefig and the print message so it matches how plot_dashboard and
plot_cumulative_savings construct paths and avoids relying on a trailing slash.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@test/test_plotter.py`:
- Around line 295-306: Tests reference a non-existent pytest fixture output_dir
causing failures; update TestPlotEpisode.test_saves_file (and
test_with_plot_flags_disabled if present) to use tempfile.TemporaryDirectory()
inline like test_no_save: import tempfile, create a TemporaryDirectory()
context, set env.plots_dir to f"{tmpdir}/" (ensuring trailing slash), call
plot_episode(env, ... , save=True, show=False, suffix=1) and then inspect files
in that temporary directory; alternatively add a simple output_dir fixture to
conftest.py that yields a TemporaryDirectory path, but prefer the inline
TemporaryDirectory approach for consistency with test_no_save and to keep tests
self-contained.

---

Nitpick comments:
In `@src/plotter.py`:
- Around line 172-175: The save path in the plot_episode save block uses string
concatenation and assumes a trailing slash; change the code in the save branch
(the plt.savefig and subsequent print) to build the filename with
os.path.join(env.plots_dir, f"{prefix}_{suffix:09d}_{timestamp}.png") and reuse
that joined path for both plt.savefig and the print message so it matches how
plot_dashboard and plot_cumulative_savings construct paths and avoids relying on
a trailing slash.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1bed9f9c-f521-41f3-8966-ffa823de7845

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7adec and 262b68f.

📒 Files selected for processing (7)
  • .github/workflows/tests.yml
  • src/environment.py
  • src/plot.py
  • src/plot_config.py
  • src/plotter.py
  • test/test_plotter.py
  • train.py
💤 Files with no reviewable changes (2)
  • src/plot_config.py
  • src/plot.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/plotter.py (2)

89-116: Potential shape mismatch when metrics lists are shorter than num_hours.

The hours array has num_hours elements, but env.metrics.episode_* lists may have fewer entries if the episode hasn't completed all hours yet or if metrics collection started later. Matplotlib will raise a ValueError when plotting arrays of different lengths.

Consider using _as_series() (already defined in this file) to pad/truncate metrics to the expected length, similar to how plot_dashboard handles this:

♻️ Proposed fix using _as_series for safe array handling
 def plot_episode(env: ComputeClusterEnv, num_hours: int, max_nodes: int, save: bool = True, show: bool = True, suffix: int = 0) -> None:
     hours = np.arange(num_hours)
     fig, ax1 = plt.subplots(figsize=(12, 6))
 
     color = 'tab:blue'
     ax1.set_xlabel('Hours')
     ax1.set_ylabel('Electricity Price (€/MWh)', color=color)
     if env.plot_config.plot_price:
-        ax1.plot(hours, env.metrics.episode_price_stats, color=color, label='Electricity Price (€/MWh)')
+        ax1.plot(hours, _as_series(env.metrics.episode_price_stats, num_hours), color=color, label='Electricity Price (€/MWh)')
     ax1.tick_params(axis='y', labelcolor=color)
 
     ax2 = ax1.twinx()
     ax2.set_ylabel('Count / Rewards', color='tab:orange')
 
     if env.plot_config.plot_online_nodes:
-        ax2.plot(hours, env.metrics.episode_on_nodes, color='orange', label='Online Nodes')
+        ax2.plot(hours, _as_series(env.metrics.episode_on_nodes, num_hours), color='orange', label='Online Nodes')
     if env.plot_config.plot_used_nodes:
-        ax2.plot(hours, env.metrics.episode_used_nodes, color='green', label='Used Nodes')
+        ax2.plot(hours, _as_series(env.metrics.episode_used_nodes, num_hours), color='green', label='Used Nodes')
     if env.plot_config.plot_job_queue:
-        ax2.plot(hours, env.metrics.episode_job_queue_sizes, color='red', label='Job Queue Size')
+        ax2.plot(hours, _as_series(env.metrics.episode_job_queue_sizes, num_hours), color='red', label='Job Queue Size')
 
     if env.plot_config.plot_eff_reward:
-        ax2.plot(hours, env.metrics.episode_eff_rewards, color='brown', linestyle='--', label='Efficiency Rewards')
+        ax2.plot(hours, _as_series(env.metrics.episode_eff_rewards, num_hours), color='brown', linestyle='--', label='Efficiency Rewards')
     if env.plot_config.plot_price_reward:
-        ax2.plot(hours, env.metrics.episode_price_rewards, color='blue', linestyle='--', label='Price Rewards')
+        ax2.plot(hours, _as_series(env.metrics.episode_price_rewards, num_hours), color='blue', linestyle='--', label='Price Rewards')
     if env.plot_config.plot_idle_penalty:
-        ax2.plot(hours, env.metrics.episode_idle_penalties, color='green', linestyle='--', label='Idle Penalties')
+        ax2.plot(hours, _as_series(env.metrics.episode_idle_penalties, num_hours), color='green', linestyle='--', label='Idle Penalties')
     if env.plot_config.plot_job_age_penalty:
-        ax2.plot(hours, env.metrics.episode_job_age_penalties, color='yellow', linestyle='--', label='Job Age Penalties')
+        ax2.plot(hours, _as_series(env.metrics.episode_job_age_penalties, num_hours), color='yellow', linestyle='--', label='Job Age Penalties')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plotter.py` around lines 89 - 116, The plotting code uses hours =
np.arange(num_hours) but directly plots env.metrics.episode_* lists which can be
shorter, causing ValueError; update each plot call in the block (e.g., plotting
episode_price_stats, episode_on_nodes, episode_used_nodes,
episode_job_queue_sizes, episode_eff_rewards, episode_price_rewards,
episode_idle_penalties, episode_job_age_penalties) to pass their values through
the existing _as_series(obj, name, length) helper (the same approach used by
plot_dashboard) so each metric is padded/truncated to num_hours before plotting;
ensure you call _as_series(env.metrics, 'episode_price_stats', num_hours) etc.
and use the returned sequence for the respective ax.plot calls.

172-175: Inconsistent path construction—use os.path.join for robustness.

plot_dashboard (line 329) and plot_cumulative_savings (line 414) use os.path.join(), but this function uses string concatenation. This creates an inconsistency and could fail if plots_dir doesn't end with a trailing slash.

♻️ Proposed fix for consistent path handling
     if save:
         timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
-        plt.savefig(f"{env.plots_dir}{prefix}_ep_{suffix:09d}_{timestamp}.png")
-        print(f"Figure saved as: {env.plots_dir}{prefix}_ep_{suffix:09d}_{timestamp}.png\nExpecting next save after {env.next_plot_save + env.steps_per_iteration}")
+        fname = f"{prefix}_ep_{suffix:09d}_{timestamp}.png"
+        save_path = os.path.join(env.plots_dir, fname)
+        plt.savefig(save_path)
+        print(f"Figure saved as: {save_path}\nExpecting next save after {env.next_plot_save + env.steps_per_iteration}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plotter.py` around lines 172 - 175, The save path is built via string
concatenation which is fragile; change the plt.savefig call to build the
filepath with os.path.join(env.plots_dir,
f"{prefix}_ep_{suffix:09d}_{timestamp}.png") (and update the printed message to
use that same joined filepath) so it matches
plot_dashboard/plot_cumulative_savings behavior; ensure you import os if not
already and keep the filename formatting using prefix, suffix, and timestamp as
before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/plotter.py`:
- Around line 89-116: The plotting code uses hours = np.arange(num_hours) but
directly plots env.metrics.episode_* lists which can be shorter, causing
ValueError; update each plot call in the block (e.g., plotting
episode_price_stats, episode_on_nodes, episode_used_nodes,
episode_job_queue_sizes, episode_eff_rewards, episode_price_rewards,
episode_idle_penalties, episode_job_age_penalties) to pass their values through
the existing _as_series(obj, name, length) helper (the same approach used by
plot_dashboard) so each metric is padded/truncated to num_hours before plotting;
ensure you call _as_series(env.metrics, 'episode_price_stats', num_hours) etc.
and use the returned sequence for the respective ax.plot calls.
- Around line 172-175: The save path is built via string concatenation which is
fragile; change the plt.savefig call to build the filepath with
os.path.join(env.plots_dir, f"{prefix}_ep_{suffix:09d}_{timestamp}.png") (and
update the printed message to use that same joined filepath) so it matches
plot_dashboard/plot_cumulative_savings behavior; ensure you import os if not
already and keep the filename formatting using prefix, suffix, and timestamp as
before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb221669-1377-47fe-a96c-52648d6e4920

📥 Commits

Reviewing files that changed from the base of the PR and between 262b68f and 15bfc22.

📒 Files selected for processing (1)
  • src/plotter.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/environment.py`:
- Around line 279-287: The reset() method currently leaves timeline state from
the previous episode; call _reset_timeline_state() at the start of reset() (or
explicitly clear backlog_queue and reinitialize state keys nodes, job_queue,
pending_job_count, pending_core_hours, pending_avg_duration, pending_max_nodes,
backlog_size) so each episode begins with a fresh 336-hour timeline; update
reset() to invoke _reset_timeline_state() before metrics.reset_episode_metrics()
and the price handling (or perform equivalent explicit clears) to ensure full
episode isolation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe173bcc-bedb-4068-847a-28b7a0442d9c

📥 Commits

Reviewing files that changed from the base of the PR and between 15bfc22 and 45157b6.

📒 Files selected for processing (5)
  • analyze_lambda_occupancy.py
  • src/environment.py
  • test/test_sanity_env.py
  • train.py
  • train_iter.py
💤 Files with no reviewable changes (1)
  • analyze_lambda_occupancy.py

@rbx rbx force-pushed the plotter-cleanup branch from 45157b6 to e8f3957 Compare March 9, 2026 09:55
@rbx rbx merged commit 1e983e8 into master Mar 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant