File size: 8,112 Bytes
df06d5c
 
2982a51
281711d
2dafeb1
1bcb06b
211c032
688f116
df06d5c
281711d
9a87acd
84fdef4
2dafeb1
509301c
672339b
10e69e7
22f82e7
61fa714
15ae508
61fa714
 
 
688f116
84fdef4
3edbc93
cfa5138
8f9985e
2dafeb1
ba1131a
069fb2c
 
 
de75bee
1bcb06b
8f9985e
10e69e7
177a597
 
 
 
6836e69
a3fddce
 
 
4808b6b
 
 
 
 
 
509301c
4808b6b
 
2dafeb1
61fa714
 
177a597
1bcb06b
8f9985e
fe04bb9
10e69e7
1bcb06b
5b5ee28
10e69e7
de9585b
4a37ad2
de9585b
 
069fb2c
2dafeb1
069fb2c
2dafeb1
 
61fa714
de9585b
8f9985e
a3fddce
1bcb06b
de9585b
 
2982a51
211c032
4a37ad2
 
211c032
 
 
cfa5138
4a37ad2
df06d5c
4a37ad2
a897a54
5554fb7
5135eea
6c94821
2dafeb1
22f82e7
2dafeb1
22f82e7
 
a3fddce
22f82e7
3d4c9af
 
 
 
0fdb208
27f3da5
2dafeb1
3d4c9af
2dafeb1
22f82e7
 
21f87d6
2dafeb1
767c884
2dafeb1
 
0fdb208
27f3da5
 
2dafeb1
3edbc93
a897a54
27f3da5
 
a897a54
 
 
 
27f3da5
 
 
 
 
 
 
 
 
 
 
 
 
8f9985e
2dafeb1
 
 
8f9985e
2dafeb1
 
 
688f116
672339b
27f3da5
2dafeb1
8f9985e
de9585b
211c032
 
61fa714
22f82e7
 
 
 
 
95a9631
 
 
 
 
 
2dafeb1
ba1131a
 
15ae508
ba1131a
688f116
 
ba1131a
 
8f9985e
ba1131a
281711d
 
6836e69
fb66aff
 
6836e69
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import time

import pandas as pd
import gradio as gr
from gradio.themes.utils import sizes
from gradio_leaderboard import Leaderboard
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file (before imports)

from about import ABOUT_INTRO, ABOUT_TEXT, FAQS, SUBMIT_INSTRUCTIONS, WEBSITE_HEADER
from constants import (
    ASSAY_RENAME,  # noqa: F401
    SECOND_DEADLINE,
    SEQUENCES_FILE_DICT,
    LEADERBOARD_DISPLAY_COLUMNS,
    ABOUT_TAB_NAME,
    FAQ_TAB_NAME,
    TERMS_URL,
    LEADERBOARD_COLUMNS_RENAME,
    LEADERBOARD_COLUMNS_RENAME_LIST,
    SUBMIT_TAB_NAME,
    SLACK_URL,
)
from submit import make_submission
from utils import fetch_hf_results, show_output_box, periodic_data_fetch


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"].apply(lambda x: f"{x:.3f}")
    # Cast submission_time to datetime
    df["submission_time"] = pd.to_datetime(df["submission_time"], errors="coerce")
    # Before the first deadline: Say we're busy evaluating
    for metric in ["spearman", "top_10_recall"]:
        if metric not in df.columns:
            continue
        df.loc[
            (df["dataset"] == "Heldout Test Set")
            & (df[metric] == "nan")
            & (df["submission_time"] <= SECOND_DEADLINE),
            metric,
        ] = "Error: All predictions have a constant value"

    # 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")
    # 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
    current_dataframe = pd.read_csv("debug-current-results.csv")
    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,
        height=500,  # Set a fixed height to make it scrollable
    )
    return lb


def refresh_overall_leaderboard():
    # debug-current-results.csv is updated by the outer thread
    current_dataframe = pd.read_csv("debug-current-results.csv")
    return format_leaderboard_table(df_results=current_dataframe)


# Initialize global dataframe
fetch_hf_results()
time.sleep(2)  # Give the outer thread time to create the file at the start
current_dataframe = pd.read_csv("debug-current-results.csv")

# 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

    ## Header

    with gr.Row():
        with gr.Column(scale=6):  # bigger text area
            gr.Markdown(WEBSITE_HEADER)
        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,
                show_fullscreen_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_v3.png",
                show_label=False,
                show_download_button=False,
                show_share_button=False,
                show_fullscreen_button=False,
                width="30vw",
            )
            gr.Markdown(ABOUT_TEXT)

            # Sequence download buttons
            gr.Markdown(
                """### ๐Ÿ“ฅ Download Sequences
            The GDPa1 dataset (with assay data and sequences) is available on Hugging Face [here](https://huggingface.co/datasets/ginkgo-datapoints/GDPa1),
            but we provide this and the private test set for convenience."""
            )
            with gr.Row():
                with gr.Column():
                    download_button_cv_about = gr.DownloadButton(
                        label="๐Ÿ“ฅ Download GDPa1 sequences",
                        value=SEQUENCES_FILE_DICT["GDPa1_cross_validation"],
                        variant="secondary",
                    )
                with gr.Column():
                    download_button_test_about = gr.DownloadButton(
                        label="๐Ÿ“ฅ Download Private Test Set sequences",
                        value=SEQUENCES_FILE_DICT["Heldout Test Set"],
                        variant="secondary",
                    )

        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 *easy to overfit* the public GDPa1 dataset, which results in artificially high Spearman correlations.
                We would suggest training using cross-validation to give a better indication of the model's performance on the eventual private test set.
                """
            )
            lb = get_leaderboard_object()
            timer.tick(fn=refresh_overall_leaderboard, outputs=lb)
            demo.load(fn=refresh_overall_leaderboard, outputs=lb)
        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):
                    if isinstance(answer, list):
                        # Italicize each line
                        italicized_answer = "  \n".join(f"*{item}*" for item in answer)
                        gr.Markdown(italicized_answer)
                    else:
                        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 discuss on the <a href="{SLACK_URL}">Slack community</a> co-hosted by Bits in Bio.<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, and see Terms <a href="{TERMS_URL}">here</a>.
        </div>
        """,
        elem_id="contact-footer",
    )

if __name__ == "__main__":
    demo.launch(
        ssr_mode=False,
        app_kwargs={"lifespan": periodic_data_fetch},
    )