Skip to content
Closed
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
21 changes: 20 additions & 1 deletion ax/analysis/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
AnalysisCardBase,
AnalysisCardGroup,
ErrorAnalysisCard,
NotApplicableStateAnalysisCard,
)
from ax.core.experiment import Experiment
from ax.exceptions.analysis import AnalysisNotApplicableStateError
Expand All @@ -29,6 +30,12 @@

logger: Logger = get_logger(__name__)

NOT_APPLICABLE_STATE_SUBTITLE: str = (
"This analysis is temporarily unavailable. It will become available "
"as your experiment progresses (e.g., after collecting more data, "
"running more trials, or fitting a model)."
)


class Analysis(Protocol):
"""
Expand Down Expand Up @@ -202,7 +209,7 @@ def display_cards(

def error_card_from_analysis_e(
analysis_e: AnalysisE,
) -> ErrorAnalysisCard:
) -> ErrorAnalysisCard | NotApplicableStateAnalysisCard:
analysis_name = analysis_e.analysis.__class__.__name__
exception_name = analysis_e.exception.__class__.__name__

Expand All @@ -213,6 +220,18 @@ def error_card_from_analysis_e(
if (exception_message := str(analysis_e.exception))
else f"{exception_name} encountered while computing {analysis_name}."
)
if isinstance(analysis_e.exception, AnalysisNotApplicableStateError):
# AnalysisNotApplicableStateError gets rendered as a
# NotApplicableStateAnalysisCard
return NotApplicableStateAnalysisCard(
name=analysis_name,
title=f"{analysis_name} -- Not Available Yet",
subtitle=NOT_APPLICABLE_STATE_SUBTITLE,
df=pd.DataFrame(),
blob=exception_message
if exception_message
else f"{exception_name} encountered.",
)

return ErrorAnalysisCard(
name=analysis_name,
Expand Down
35 changes: 31 additions & 4 deletions ax/analysis/tests/test_analysis.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
#!/usr/bin/env python3
# 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-strict

from ax.analysis.analysis import AnalysisE, error_card_from_analysis_e
from ax.analysis.analysis import (
AnalysisE,
error_card_from_analysis_e,
NOT_APPLICABLE_STATE_SUBTITLE,
)
from ax.analysis.plotly.parallel_coordinates import ParallelCoordinatesPlot
from ax.core.analysis_card import ErrorAnalysisCard, NotApplicableStateAnalysisCard
from ax.exceptions.analysis import AnalysisNotApplicableStateError
from ax.utils.common.testutils import TestCase


class AnalysisTest(TestCase):
def test_error_card_from_analysis_e(self) -> None:
for exception, expected_subtitle in (
for (
exception,
expected_card_type,
expected_title,
expected_subtitle,
expected_blob,
) in (
(
ValueError("something went wrong"),
ErrorAnalysisCard,
"ParallelCoordinatesPlot Error",
"ValueError: something went wrong",
"ValueError",
),
(
ValueError(),
ErrorAnalysisCard,
"ParallelCoordinatesPlot Error",
"ValueError encountered while computing ParallelCoordinatesPlot.",
"ValueError",
),
(
AnalysisNotApplicableStateError("Experiment has no data."),
NotApplicableStateAnalysisCard,
"ParallelCoordinatesPlot -- Not Available Yet",
NOT_APPLICABLE_STATE_SUBTITLE,
"Experiment has no data.",
),
):
with self.subTest(exception=exception):
Expand All @@ -31,7 +57,8 @@ def test_error_card_from_analysis_e(self) -> None:

card = error_card_from_analysis_e(analysis_e)

self.assertIsInstance(card, expected_card_type)
self.assertEqual(card.name, "ParallelCoordinatesPlot")
self.assertEqual(card.title, "ParallelCoordinatesPlot Error")
self.assertEqual(card.title, expected_title)
self.assertEqual(card.subtitle, expected_subtitle)
self.assertIn("ValueError", card.blob)
self.assertIn(expected_blob, card.blob)
5 changes: 3 additions & 2 deletions ax/api/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import numpy as np
import pandas as pd
from ax.analysis.analysis import NOT_APPLICABLE_STATE_SUBTITLE
from ax.analysis.plotly.parallel_coordinates import ParallelCoordinatesPlot
from ax.api.client import Client
from ax.api.configs import (
Expand Down Expand Up @@ -1278,10 +1279,10 @@ def test_compute_analyses(self) -> None:

self.assertEqual(len(cards), 1)
self.assertEqual(cards[0].name, "ParallelCoordinatesPlot")
self.assertEqual(cards[0].title, "ParallelCoordinatesPlot Error")
self.assertEqual(cards[0].title, "ParallelCoordinatesPlot -- Not Available Yet")
self.assertEqual(
cards[0].subtitle,
"AnalysisNotApplicableStateError: Experiment has no trials.",
NOT_APPLICABLE_STATE_SUBTITLE,
)

for trial_index, _ in client.get_next_trials(max_trials=1).items():
Expand Down