Skip to content

Commit 4b8ab75

Browse files
Jelena Markovic-Voronovfacebook-github-bot
Jelena Markovic-Voronov
authored andcommitted
storage for auxiliary sources (facebook#3305)
Summary: Pull Request resolved: facebook#3305 Adding proper encoder and decoder for auxiliary_experiments_by_purpose argument of Experiment object. Previously, auxiliary_experiments_by_purpose used to include only AuxiliaryExperiment object that had an easy encoder and decoder via experiment name. But after allowing AuxiliarySources to be added in auxiliary_experiments_by_purpose we need to encode and decode AuxiliarySource objects as well. Reviewed By: saitcakmak Differential Revision: D68542281 fbshipit-source-id: 27ecb24e38d814db525ee349c9b58fb316ea872b
1 parent 4ad20f7 commit 4b8ab75

File tree

3 files changed

+45
-18
lines changed

3 files changed

+45
-18
lines changed

ax/storage/sqa_store/decoder.py

+37-16
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from enum import Enum
1212
from io import StringIO
1313
from logging import Logger
14-
from typing import cast, Union
14+
from typing import Any, cast, Union
1515

1616
import pandas as pd
1717
from ax.analysis.analysis import AnalysisCard
@@ -108,29 +108,25 @@ def _auxiliary_experiments_by_purpose_from_experiment_sqa(
108108
) -> dict[AuxiliaryExperimentPurpose, list[AuxiliaryExperiment]] | None:
109109
auxiliary_experiments_by_purpose = None
110110
if experiment_sqa.auxiliary_experiments_by_purpose:
111-
from ax.storage.sqa_store.load import load_experiment
112-
113111
auxiliary_experiments_by_purpose = {}
114-
aux_exp_name_dict = none_throws(
115-
experiment_sqa.auxiliary_experiments_by_purpose
116-
)
117-
for aux_exp_purpose_str, aux_exp_names in aux_exp_name_dict.items():
112+
aux_exps_dict = none_throws(experiment_sqa.auxiliary_experiments_by_purpose)
113+
for aux_exp_purpose_str, aux_exps_json in aux_exps_dict.items():
118114
aux_exp_purpose = next(
119115
member
120116
for member in self.config.auxiliary_experiment_purpose_enum
121117
if member.value == aux_exp_purpose_str
122118
)
123119
auxiliary_experiments_by_purpose[aux_exp_purpose] = []
124-
for aux_exp_name in aux_exp_names:
120+
for aux_exp_json in aux_exps_json:
121+
# keeping this for backward compatibility since previously
122+
# we used to save only the experiment name
123+
if isinstance(aux_exp_json, str):
124+
aux_exp_json = {"experiment_name": aux_exp_json}
125+
aux_experiment = auxiliary_experiment_from_json(
126+
json=aux_exp_json, config=self.config
127+
)
125128
auxiliary_experiments_by_purpose[aux_exp_purpose].append(
126-
AuxiliaryExperiment(
127-
experiment=load_experiment(
128-
aux_exp_name,
129-
config=self.config,
130-
skip_runners_and_metrics=True,
131-
load_auxiliary_experiments=False,
132-
)
133-
)
129+
aux_experiment
134130
)
135131
return auxiliary_experiments_by_purpose
136132

@@ -1321,3 +1317,28 @@ def _get_scalarized_outcome_constraint_children_metrics(
13211317
)
13221318
metrics_sqa = query.all()
13231319
return metrics_sqa
1320+
1321+
1322+
def auxiliary_experiment_from_json(
1323+
json: dict[str, Any], config: SQAConfig
1324+
) -> AuxiliaryExperiment:
1325+
"""
1326+
Load an ``AuxiliaryExperiment`` from JSON.
1327+
1328+
Args:
1329+
json: A dictionary containing the JSON representation of an AuxiliaryExperiment.
1330+
config: The SQAConfig object used to load the experiment.
1331+
1332+
Returns:
1333+
An AuxiliaryExperiment object constructed from the JSON representation.
1334+
"""
1335+
1336+
from ax.storage.sqa_store.load import load_experiment
1337+
1338+
experiment = load_experiment(
1339+
json.get("experiment_name"),
1340+
config=config,
1341+
skip_runners_and_metrics=True,
1342+
load_auxiliary_experiments=False,
1343+
)
1344+
return AuxiliaryExperiment(experiment)

ax/storage/sqa_store/encoder.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,13 @@ def experiment_to_sqa(self, experiment: Experiment) -> SQAExperiment:
188188
aux_exps,
189189
) in experiment.auxiliary_experiments_by_purpose.items():
190190
aux_exp_type = aux_exp_type_enum.value
191-
aux_exp_jsons = [aux_exp.experiment.name for aux_exp in aux_exps]
191+
aux_exp_jsons = [
192+
{
193+
"__type": aux_exp.__class__.__name__,
194+
"experiment_name": aux_exp.experiment.name,
195+
}
196+
for aux_exp in aux_exps
197+
]
192198
auxiliary_experiments_by_purpose[aux_exp_type] = aux_exp_jsons
193199

194200
properties = experiment._properties

ax/storage/sqa_store/sqa_classes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ class SQAExperiment(Base):
371371
# pyre-fixme[8]: Incompatible attribute type [8]: Attribute
372372
# `auxiliary_experiments_by_purpose` declared in class `SQAExperiment` has
373373
# type `Optional[Dict[str, List[str]]]` but is used as type `Column[typing.Any]`
374-
auxiliary_experiments_by_purpose: dict[str, List[str]] | None = Column(
374+
auxiliary_experiments_by_purpose: dict[str, List[dict[str, Any]]] | None = Column(
375375
JSONEncodedTextDict, nullable=True, default={}
376376
)
377377

0 commit comments

Comments
 (0)