Skip to content

Commit 30a360b

Browse files
CristianLarafacebook-github-bot
authored andcommitted
Introduce ExperimentStatus class (facebook#4737)
Summary: # Summary Add `ExperimentStatus` enum with phases: DRAFT, INITIALIZATION, OPTIMIZATION, COMPLETED. **Backward compatibility:** The status field defaults to `None` for existing experiments, maintaining full backward compatibility. New experiments can optionally set status as needed. Differential Revision: D86801911
1 parent 60f5ca9 commit 30a360b

2 files changed

Lines changed: 155 additions & 0 deletions

File tree

ax/core/experiment_status.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
#
4+
# This source code is licensed under the MIT license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
# pyre-strict
8+
9+
from __future__ import annotations
10+
11+
from enum import Enum
12+
13+
14+
class ExperimentStatus(int, Enum):
15+
"""Enum of experiment status.
16+
17+
General lifecycle of an experiment is:::
18+
19+
DRAFT --> INITIALIZATION --> OPTIMIZATION --> COMPLETED
20+
21+
Experiment is marked as ``DRAFT`` immediately upon its creation when
22+
the experiment is still being configured (search space, optimization config, etc.).
23+
24+
Once the experiment is fully configured and begins initial exploration,
25+
it transitions to ``INITIALIZATION``. This is typically when the first trials
26+
are being generated to explore the search space.
27+
28+
After initial exploration completes (typically after some data has been collected),
29+
the experiment transitions to ``OPTIMIZATION``, where Bayesian optimization or
30+
other adaptive methods are used to find optimal configurations.
31+
32+
``COMPLETED`` indicates the experiment has successfully finished its objectives.
33+
34+
Note: This status tracks the high-level experiment lifecycle and is independent
35+
of individual trial statuses. An experiment in OPTIMIZATION status may have
36+
trials in various states (RUNNING, COMPLETED, FAILED, etc.).
37+
"""
38+
39+
DRAFT = 0
40+
INITIALIZATION = 1
41+
OPTIMIZATION = 2
42+
COMPLETED = 4
43+
44+
@property
45+
def is_terminal(self) -> bool:
46+
"""True if experiment is finished and cannot continue."""
47+
return self == ExperimentStatus.COMPLETED
48+
49+
@property
50+
def is_active(self) -> bool:
51+
"""True if experiment is actively running trials."""
52+
return (
53+
self == ExperimentStatus.INITIALIZATION
54+
or self == ExperimentStatus.OPTIMIZATION
55+
)
56+
57+
@property
58+
def is_draft(self) -> bool:
59+
"""True if experiment is in draft phase."""
60+
return self == ExperimentStatus.DRAFT
61+
62+
@property
63+
def is_initialization(self) -> bool:
64+
"""True if experiment is in initialization phase."""
65+
return self == ExperimentStatus.INITIALIZATION
66+
67+
@property
68+
def is_optimization(self) -> bool:
69+
"""True if experiment is in optimization phase."""
70+
return self == ExperimentStatus.OPTIMIZATION
71+
72+
@property
73+
def is_completed(self) -> bool:
74+
"""True if experiment has successfully completed."""
75+
return self == ExperimentStatus.COMPLETED
76+
77+
def __format__(self, fmt: str) -> str:
78+
"""Define `__format__` to avoid pulling the `__format__` from the `int`
79+
mixin (since its better for statuses to show up as `DRAFT` than as
80+
just an int that is difficult to interpret).
81+
82+
E.g. experiment representation with the overridden method is:
83+
"Experiment(name='test', status=ExperimentStatus.DRAFT)".
84+
85+
Docs on enum formatting: https://docs.python.org/3/library/enum.html#others.
86+
"""
87+
return f"{self!s}"
88+
89+
def __repr__(self) -> str:
90+
return f"{self.__class__.__name__}.{self.name}"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
#
4+
# This source code is licensed under the MIT license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
# pyre-strict
8+
9+
from ax.core.experiment_status import ExperimentStatus
10+
from ax.utils.common.testutils import TestCase
11+
12+
13+
class TestExperimentStatus(TestCase):
14+
"""Tests for the ExperimentStatus enum."""
15+
16+
def test_status_values(self) -> None:
17+
"""Test that status enum values are correctly defined."""
18+
self.assertEqual(ExperimentStatus.DRAFT.value, 0)
19+
self.assertEqual(ExperimentStatus.INITIALIZATION.value, 1)
20+
self.assertEqual(ExperimentStatus.OPTIMIZATION.value, 2)
21+
self.assertEqual(ExperimentStatus.COMPLETED.value, 4)
22+
23+
def test_is_terminal(self) -> None:
24+
"""Test the is_terminal property."""
25+
# Terminal statuses
26+
self.assertTrue(ExperimentStatus.COMPLETED.is_terminal)
27+
28+
# Non-terminal statuses
29+
self.assertFalse(ExperimentStatus.DRAFT.is_terminal)
30+
self.assertFalse(ExperimentStatus.INITIALIZATION.is_terminal)
31+
self.assertFalse(ExperimentStatus.OPTIMIZATION.is_terminal)
32+
33+
def test_is_active(self) -> None:
34+
"""Test the is_active property."""
35+
# Active statuses
36+
self.assertTrue(ExperimentStatus.INITIALIZATION.is_active)
37+
self.assertTrue(ExperimentStatus.OPTIMIZATION.is_active)
38+
39+
# Inactive statuses
40+
self.assertFalse(ExperimentStatus.DRAFT.is_active)
41+
self.assertFalse(ExperimentStatus.COMPLETED.is_active)
42+
43+
def test_individual_status_checks(self) -> None:
44+
"""Test individual status check properties."""
45+
self.assertTrue(ExperimentStatus.DRAFT.is_draft)
46+
self.assertFalse(ExperimentStatus.INITIALIZATION.is_draft)
47+
48+
self.assertTrue(ExperimentStatus.INITIALIZATION.is_initialization)
49+
self.assertFalse(ExperimentStatus.OPTIMIZATION.is_initialization)
50+
51+
self.assertTrue(ExperimentStatus.OPTIMIZATION.is_optimization)
52+
self.assertFalse(ExperimentStatus.COMPLETED.is_optimization)
53+
54+
self.assertTrue(ExperimentStatus.COMPLETED.is_completed)
55+
self.assertFalse(ExperimentStatus.DRAFT.is_completed)
56+
57+
def test_format_and_repr(self) -> None:
58+
"""Test __format__ and __repr__ methods."""
59+
status = ExperimentStatus.DRAFT
60+
self.assertEqual(f"{status}", "ExperimentStatus.DRAFT")
61+
self.assertEqual(repr(status), "ExperimentStatus.DRAFT")
62+
63+
status = ExperimentStatus.OPTIMIZATION
64+
self.assertEqual(f"{status}", "ExperimentStatus.OPTIMIZATION")
65+
self.assertEqual(repr(status), "ExperimentStatus.OPTIMIZATION")

0 commit comments

Comments
 (0)