Source code for ax.analysis.healthcheck.can_generate_candidates
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import json
from datetime import datetime
from typing import Optional
import pandas as pd
from ax.analysis.analysis import AnalysisCardLevel
from ax.analysis.healthcheck.healthcheck_analysis import (
HealthcheckAnalysis,
HealthcheckAnalysisCard,
HealthcheckStatus,
)
from ax.core.experiment import Experiment
from ax.core.generation_strategy_interface import GenerationStrategyInterface
from pyre_extensions import none_throws
[docs]
class CanGenerateCandidatesAnalysis(HealthcheckAnalysis):
REASON_PREFIX: str = "This experiment cannot generate candidates.\nREASON: "
LAST_RUN_TEMPLATE: str = "\n\nLAST TRIAL RUN: {days} day(s) ago"
def __init__(
self, can_generate_candidates: bool, reason: str, days_till_fail: int
) -> None:
"""
Args:
can_generate_candidates: Whether the experiment can generate candidates. If
True, the status is automatically set to PASS. If False, this
``Analysis`` will check when last trial was run and compare it to the
threshold of ``days_till_fail``.
reason: The reason why the experiment cannot generate candidates, or
statement that it can.
days_till_fail: The number of days since the last trial was run before
the status is set to FAIL.
"""
self.can_generate_candidates = can_generate_candidates
self.reason = reason
self.days_till_fail = days_till_fail
[docs]
def compute(
self,
experiment: Optional[Experiment] = None,
generation_strategy: GenerationStrategyInterface | None = None,
) -> HealthcheckAnalysisCard:
status = HealthcheckStatus.PASS
subtitle = self.reason
title_status = "Success"
level = AnalysisCardLevel.LOW
if not self.can_generate_candidates:
subtitle = f"{self.REASON_PREFIX}{self.reason}"
most_recent_run_time = max(
[
t.time_run_started
for t in none_throws(experiment).trials.values()
if t.time_run_started is not None
],
default=None,
)
if most_recent_run_time is None:
status = HealthcheckStatus.FAIL
level = AnalysisCardLevel.HIGH
title_status = "Failure"
else:
days_since_last_run = (datetime.now() - most_recent_run_time).days
if days_since_last_run > self.days_till_fail:
status = HealthcheckStatus.FAIL
level = AnalysisCardLevel.HIGH
title_status = "Failure"
else:
status = HealthcheckStatus.WARNING
level = AnalysisCardLevel.MID
title_status = "Warning"
subtitle += self.LAST_RUN_TEMPLATE.format(days=days_since_last_run)
return HealthcheckAnalysisCard(
name="CanGenerateCandidates",
title=f"Ax Candidate Generation {title_status}",
blob=json.dumps(
{
"status": status,
}
),
subtitle=subtitle,
df=pd.DataFrame(
{
"status": [status],
"reason": [self.reason],
}
),
level=level,
)