Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.ipynb linguist-vendored
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ keywords:
- data-science
- Python
license: GPL-3.0
version: 2.1.4
version: 2.1.5
date-released: 2025-11-17
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
project = 'Hill Climber'
copyright = '2025, Hill Climber Contributors'
author = 'Hill Climber Contributors'
release = '2.1.4'
release = '2.1.5'

# -- General configuration ---------------------------------------------------
extensions = [
Expand Down
2 changes: 1 addition & 1 deletion hill_climber/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
>>> plot_optimization_results('checkpoint.pkl', all_replicas=True)
"""

__version__ = '2.1.4'
__version__ = '2.1.5'
__author__ = 'gperdrizet'

from .optimizer import HillClimber
Expand Down
35 changes: 18 additions & 17 deletions hill_climber/dashboard_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,27 @@ def load_metrics_history(
return pd.DataFrame()

try:
# Check total steps to determine if downsampling is needed
count_query = "SELECT MAX(step) as max_step FROM metrics_history"
result = pd.read_sql_query(count_query, conn)
if result.empty or result['max_step'].iloc[0] is None:
# Load all metrics for the requested metric names
placeholders = ','.join(['?' for _ in metric_names])
query = f"SELECT replica_id, step, metric_name, value FROM metrics_history WHERE metric_name IN ({placeholders}) ORDER BY replica_id, step, metric_name"
df = pd.read_sql_query(query, conn, params=metric_names)

if df.empty:
return pd.DataFrame()

total_steps = int(result['max_step'].iloc[0])

# Build query with optional downsampling
if total_steps > max_points_per_replica:
step_interval = max(1, total_steps // max_points_per_replica)
query = f"SELECT replica_id, step, metric_name, value FROM metrics_history WHERE step % {step_interval} = 0"
else:
query = "SELECT replica_id, step, metric_name, value FROM metrics_history"

# Filter by requested metrics
placeholders = ','.join(['?' for _ in metric_names])
query += f" AND metric_name IN ({placeholders}) ORDER BY replica_id, step, metric_name"
# Downsample per replica to ensure all replicas are represented
# Group by replica_id and metric_name, then sample rows
downsampled_dfs = []
for (replica_id, metric_name), group in df.groupby(['replica_id', 'metric_name']):
if len(group) > max_points_per_replica:
# Sample evenly: keep every Nth row
step_size = len(group) / max_points_per_replica
indices = [int(i * step_size) for i in range(max_points_per_replica)]
downsampled_dfs.append(group.iloc[indices])
else:
downsampled_dfs.append(group)

return pd.read_sql_query(query, conn, params=metric_names)
return pd.concat(downsampled_dfs, ignore_index=True) if downsampled_dfs else pd.DataFrame()
except Exception:
return pd.DataFrame()

Expand Down
25 changes: 20 additions & 5 deletions hill_climber/dashboard_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ def create_replica_plot(
Returns:
go.Figure: Plotly Figure object with replica metrics plot.
"""
# Calculate total number of legend entries
total_metrics = 1 + len(additional_metrics) # objective + additional
if show_exchanges:
total_metrics += 1 # add exchange marker

# Base height + additional height per metric (beyond 1)
base_height = 350
height_per_metric = 35
plot_height = base_height + (total_metrics - 1) * height_per_metric

# Calculate top margin based on number of metrics
base_top_margin = 90
margin_per_metric = 20
top_margin = base_top_margin + (total_metrics - 1) * margin_per_metric

fig = make_subplots(specs=[[{"secondary_y": True}]])

# Color palette
Expand Down Expand Up @@ -67,7 +82,7 @@ def create_replica_plot(
fig.add_trace(
go.Scatter(
x=batch_numbers, y=y_values, mode='lines',
name=objective_metric, line=dict(color='#2E86AB', width=3),
name='Objective', line=dict(color='#2E86AB', width=3),
hovertemplate=hover_template, customdata=customdata
),
secondary_y=False
Expand Down Expand Up @@ -124,7 +139,7 @@ def create_replica_plot(
go.Scatter(
x=[None], y=[None],
mode='lines',
name='Temperature exchange',
name='Exchange',
line=dict(color='#555', width=1, dash='dash'),
showlegend=True
),
Expand All @@ -146,9 +161,9 @@ def create_replica_plot(
fig.update_layout(
title_text=title,
title_font_size=20,
legend=dict(orientation='h', yanchor='bottom', y=-0.3),
margin=dict(l=40, r=20, t=40, b=70),
height=400
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='center', x=0.5),
margin=dict(l=40, r=20, t=top_margin, b=40),
height=plot_height
)

return fig
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "parallel-hill-climber"
version = "2.1.4"
version = "2.1.5"
authors = [
{name = "gperdrizet", email = "[email protected]"},
]
Expand Down