loodvanniekerkginkgo's picture
Adding more debugging to figure out the state issues
c4f1261
import pandas as pd
import gradio as gr
from gradio.themes.utils import sizes
from gradio_leaderboard import Leaderboard
from about import ABOUT_INTRO, ABOUT_TEXT, FAQS, SUBMIT_INTRUCTIONS
from constants import (
ASSAY_RENAME, # noqa: F401
EXAMPLE_FILE_DICT,
LEADERBOARD_DISPLAY_COLUMNS,
ABOUT_TAB_NAME,
FAQ_TAB_NAME,
TERMS_URL,
LEADERBOARD_COLUMNS_RENAME,
LEADERBOARD_COLUMNS_RENAME_LIST,
SUBMIT_TAB_NAME,
)
from submit import make_submission
from utils import fetch_hf_results, show_output_box, get_time
def format_leaderboard_table(df_results: pd.DataFrame, assay: str | None = None):
"""
Format the dataframe for display on the leaderboard. The dataframe comes from utils.fetch_hf_results().
"""
df = df_results.query("assay.isin(@ASSAY_RENAME.keys())").copy()
if assay is not None:
df = df[df["assay"] == assay]
df = df[LEADERBOARD_DISPLAY_COLUMNS]
df = df.sort_values(by="spearman", ascending=False)
# After sorting, just add the reason for excluding heldout test set
# Note: We can also just say the following as a text box at the bottom of the leaderboard: "Note: Results for the Heldout Test Set are only evaluated at competition close"
# Convert spearman column to string to avoid dtype incompatibility when assigning text
df["spearman"] = df["spearman"].astype(str)
df.loc[
(df["dataset"] == "Heldout Test Set") & (df["spearman"] == "nan"), "spearman"
] = "N/A, evaluated at competition close"
# Finally, rename columns for readability
df = df.rename(columns=LEADERBOARD_COLUMNS_RENAME)
return df
def get_leaderboard_object(assay: str | None = None):
filter_columns = ["dataset"]
if assay is None:
filter_columns.append("property")
# TODO how to sort filter columns alphabetically?
# Bug: Can't leave search_columns empty because then it says "Column None not found in headers"
# Note(Lood): Would be nice to make it clear that the Search Column is searching on model name
lb = Leaderboard(
value=format_leaderboard_table(df_results=current_dataframe, assay=assay),
datatype=["str", "str", "str", "number", "str"],
select_columns=LEADERBOARD_COLUMNS_RENAME_LIST(
["model", "property", "spearman", "dataset", "user"]
),
search_columns=["Model Name"],
filter_columns=LEADERBOARD_COLUMNS_RENAME_LIST(filter_columns),
every=15,
render=True,
)
return lb
# Initialize global dataframe
current_dataframe = fetch_hf_results()
# Lood: Two problems currently:
# 1. The data_version state value isn't being incremented, it seems (even though it's triggering the dataframe change correctly)
# 2. The global current_dataframe is being shared across all sessions
# Make font size bigger using gradio theme
with gr.Blocks(theme=gr.themes.Default(text_size=sizes.text_lg)) as demo:
timer = gr.Timer(3) # Run every 3 seconds when page is focused
data_version = gr.State(value=0) # Track data changes
def update_current_dataframe():
global current_dataframe
new_dataframe = fetch_hf_results()
# Check if data has actually changed
if not current_dataframe.equals(new_dataframe):
print(f"TMP Dataframe has changed at {get_time()}. New value will be {data_version.value + 1}")
current_dataframe = new_dataframe
return data_version.value + 1 # Increment version to trigger updates
return data_version.value
timer.tick(fn=update_current_dataframe, outputs=data_version)
## Header
with gr.Row():
with gr.Column(scale=6): # bigger text area
gr.Markdown(
f"""
## Welcome to the Ginkgo Antibody Developability Benchmark!
Participants can submit their model to the leaderboards by simply uploading a CSV file (see the "✉️ Submit" tab).
You can **predict any or all of the 5 properties**, and you can filter the main leaderboard by property.
See more details in the "{ABOUT_TAB_NAME}" tab.
Submissions close on 1 November 2025.
"""
)
with gr.Column(scale=2): # smaller side column for logo
gr.Image(
value="./assets/competition_logo.jpg",
show_label=False,
show_download_button=False,
show_share_button=False,
width="25vw", # Take up the width of the column (2/8 = 1/4)
)
with gr.Tabs(elem_classes="tab-buttons"):
with gr.TabItem(ABOUT_TAB_NAME, elem_id="abdev-benchmark-tab-table"):
gr.Markdown(ABOUT_INTRO)
gr.Image(
value="./assets/prediction_explainer.png",
show_label=False,
show_download_button=False,
show_share_button=False,
width="50vw",
)
gr.Markdown(ABOUT_TEXT)
# Procedurally make these 5 tabs
# for i, assay in enumerate(ASSAY_LIST):
# with gr.TabItem(
# f"{ASSAY_EMOJIS[assay]} {ASSAY_RENAME[assay]}",
# elem_id="abdev-benchmark-tab-table",
# ) as tab_item:
# gr.Markdown(f"# {ASSAY_DESCRIPTION[assay]}")
# lb = get_leaderboard_object(assay=assay)
# def refresh_leaderboard(assay=assay):
# return format_leaderboard_table(df_results=current_dataframe, assay=assay)
# # Refresh when data version changes
# data_version.change(fn=refresh_leaderboard, outputs=lb)
# Note(Lood): Trying out just one leaderboard. We could also have a dropdown here that shows different leaderboards for each property, but that's just the same as the filters
with gr.TabItem(
"🏆 Leaderboard", elem_id="abdev-benchmark-tab-table"
) as leaderboard_tab:
gr.Markdown(
"""
# Overall Leaderboard (filter below by property)
Each property has its own prize, and participants can submit models for any combination of properties.
**Note**: It is trivial to overfit the public GDPa1 dataset, which results in very high Spearman correlations.
We would suggest training using cross-validation a limited number of times to give a better indication of the model's performance on the eventual private test set.
"""
)
lb = get_leaderboard_object()
def refresh_overall_leaderboard():
print(f"TMP Refreshing overall leaderboard at {get_time()}. Data version: {data_version.value}")
return format_leaderboard_table(df_results=current_dataframe)
# Refresh when data version changes
data_version.change(fn=refresh_overall_leaderboard, outputs=lb)
# At the bottom of the leaderboard, we can keep as NaN and explain missing test set results
# gr.Markdown(
# "_ℹ️ Results for the private test set will not be shown here and will be used for final judging at the close of the competition._"
# )
with gr.TabItem(SUBMIT_TAB_NAME, elem_id="boundary-benchmark-tab-table"):
gr.Markdown(SUBMIT_INTRUCTIONS)
submission_type_state = gr.State(value="GDPa1")
download_file_state = gr.State(value=EXAMPLE_FILE_DICT["GDPa1"])
with gr.Row():
with gr.Column():
username_input = gr.Textbox(
label="Username",
placeholder="Enter your Hugging Face username",
info="This will be used to identify valid submissions, and to update your results if you submit again.",
)
# gr.LoginButton()
anonymous_checkbox = gr.Checkbox(
label="Anonymous",
value=False,
info="If checked, your username will be replaced with an anonymous hash on the leaderboard.",
)
model_name_input = gr.Textbox(
label="Model Name",
placeholder="Enter your model name (e.g., 'MyProteinLM-v1')",
info="This will be displayed on the leaderboard.",
)
model_description_input = gr.Textbox(
label="Model Description (optional)",
placeholder="Brief description of your model and approach",
info="Describe your model, training data, or methodology.",
lines=3,
)
registration_code = gr.Textbox(
label="Registration Code",
placeholder="Enter your registration code",
info="If you did not receive a registration code, please sign up on the <a href='https://datapoints.ginkgo.bio/ai-competitions/2025-abdev-competition'>Competition Registration page</a> or email <a href='mailto:[email protected]'>[email protected]</a>.",
)
with gr.Column():
submission_type_dropdown = gr.Dropdown(
choices=["GDPa1", "GDPa1_cross_validation", "Heldout Test Set"],
value="GDPa1",
label="Submission Type",
info=f"Choose the dataset corresponding to the track you're participating in. See the '{ABOUT_TAB_NAME}' tab for details.",
)
download_button = gr.DownloadButton(
label="📥 Download example submission CSV for GDPa1",
value=EXAMPLE_FILE_DICT["GDPa1"],
variant="secondary",
)
submission_file = gr.File(label="Submission CSV")
def update_submission_type_and_file(submission_type):
"""
Based on the submission type selected in the dropdown,
Update the submission type state
Dynamically update example file for download
"""
download_file = EXAMPLE_FILE_DICT.get(
submission_type, EXAMPLE_FILE_DICT[submission_type]
)
download_label = (
f"📥 Download example submission CSV for {submission_type}"
)
return (
submission_type,
download_file,
gr.DownloadButton(
label=download_label,
value=download_file,
variant="secondary",
),
)
# Update submission type state and download button when dropdown changes
submission_type_dropdown.change(
fn=update_submission_type_and_file,
inputs=submission_type_dropdown,
outputs=[submission_type_state, download_file_state, download_button],
)
submit_btn = gr.Button("Evaluate")
message = gr.Textbox(label="Status", lines=1, visible=False)
# help message
gr.Markdown(
"If you have issues with submission or using the leaderboard, please start a discussion in the Community tab of this Space."
)
submit_btn.click(
make_submission,
inputs=[
submission_file,
username_input,
submission_type_state,
model_name_input,
model_description_input,
anonymous_checkbox,
registration_code,
],
outputs=[message],
).then(
fn=show_output_box,
inputs=[message],
outputs=[message],
)
with gr.Tab(FAQ_TAB_NAME):
gr.Markdown("# Frequently Asked Questions")
for i, (question, answer) in enumerate(FAQS.items()):
# Would love to make questions bold but accordion doesn't support it
question = f"{i+1}. {question}"
with gr.Accordion(question, open=False):
gr.Markdown(f"*{answer}*") # Italics for answers
# Footnote
gr.Markdown(
f"""
<div style="text-align: center; font-size: 14px; color: gray; margin-top: 2em;">
📬 For questions or feedback, contact <a href="mailto:[email protected]">[email protected]</a> or visit the Community tab at the top of this page.<br>
Visit the <a href="https://datapoints.ginkgo.bio/ai-competitions/2025-abdev-competition">Competition Registration page</a> to sign up for updates and to register a team, and see Terms <a href="{TERMS_URL}">here</a>.
</div>
""",
elem_id="contact-footer",
)
if __name__ == "__main__":
demo.launch(ssr_mode=False, share=True)