def generator_run_from_json(object_json: Dict[str, Any]) -> GeneratorRun: """Load Ax GeneratorRun from JSON.""" time_created_json = object_json.pop("time_created") type_json = object_json.pop("generator_run_type") index_json = object_json.pop("index") generator_run = GeneratorRun( **{k: object_from_json(v) for k, v in object_json.items()}) generator_run._time_created = object_from_json(time_created_json) generator_run._generator_run_type = object_from_json(type_json) generator_run._index = object_from_json(index_json) return generator_run
def generator_run_from_sqa(self, generator_run_sqa: SQAGeneratorRun, reduced_state: bool = False) -> GeneratorRun: """Convert SQLAlchemy GeneratorRun to Ax GeneratorRun. Args: generator_run_sqa: `SQAGeneratorRun` to decode. reduced_state: Whether to load generator runs with a slightly reduced state (without model state, search space, and optimization config). """ arms = [] weights = [] opt_config = None search_space = None for arm_sqa in generator_run_sqa.arms: arms.append(self.arm_from_sqa(arm_sqa=arm_sqa)) weights.append(arm_sqa.weight) if not reduced_state: ( opt_config, tracking_metrics, ) = self.opt_config_and_tracking_metrics_from_sqa( metrics_sqa=generator_run_sqa.metrics) if len(tracking_metrics) > 0: raise SQADecodeError( # pragma: no cover "GeneratorRun should not have tracking metrics.") search_space = self.search_space_from_sqa( parameters_sqa=generator_run_sqa.parameters, parameter_constraints_sqa=generator_run_sqa. parameter_constraints, ) best_arm_predictions = None model_predictions = None if (generator_run_sqa.best_arm_parameters is not None and generator_run_sqa.best_arm_predictions is not None): best_arm = Arm( name=generator_run_sqa.best_arm_name, parameters=not_none(generator_run_sqa.best_arm_parameters), ) best_arm_predictions = ( best_arm, tuple(not_none(generator_run_sqa.best_arm_predictions)), ) model_predictions = ( tuple(not_none(generator_run_sqa.model_predictions)) if generator_run_sqa.model_predictions is not None else None) generator_run = GeneratorRun( arms=arms, weights=weights, optimization_config=opt_config, search_space=search_space, fit_time=generator_run_sqa.fit_time, gen_time=generator_run_sqa.gen_time, best_arm_predictions=best_arm_predictions, # pyre-ignore[6] model_predictions=model_predictions, model_key=generator_run_sqa.model_key, model_kwargs=None if reduced_state else object_from_json( generator_run_sqa.model_kwargs), bridge_kwargs=None if reduced_state else object_from_json( generator_run_sqa.bridge_kwargs), gen_metadata=None if reduced_state else object_from_json( generator_run_sqa.gen_metadata), model_state_after_gen=None if reduced_state else object_from_json( generator_run_sqa.model_state_after_gen), generation_step_index=generator_run_sqa.generation_step_index, candidate_metadata_by_arm_signature=object_from_json( generator_run_sqa.candidate_metadata_by_arm_signature), ) generator_run._time_created = generator_run_sqa.time_created generator_run._generator_run_type = self.get_enum_name( value=generator_run_sqa.generator_run_type, enum=self.config.generator_run_type_enum, ) generator_run._index = generator_run_sqa.index generator_run.db_id = generator_run_sqa.id return generator_run
def trial_to_sqa(self, trial: BaseTrial) -> SQATrial: """Convert Ax Trial to SQLAlchemy. In addition to creating and storing a new Trial object, we need to create and store the GeneratorRuns and Runner that it owns. """ runner = self.runner_to_sqa( runner=trial.runner) if trial.runner else None abandoned_arms = [] generator_runs = [] status_quo_name = None if isinstance(trial, BatchTrial): abandoned_arms = [ self.abandoned_arm_to_sqa(abandoned_arm=abandoned_arm) for abandoned_arm in trial.abandoned_arms_metadata ] generator_runs = [ self.generator_run_to_sqa(generator_run=struct.generator_run, weight=struct.weight) for struct in trial.generator_run_structs ] if trial.status_quo is not None: status_quo_generator_run = GeneratorRun( arms=[trial.status_quo], weights=[trial._status_quo_weight], type=GeneratorRunType.STATUS_QUO.name, ) # this is a hack necessary to get equality tests passing; # otherwise you can encode same object and get two different results status_quo_generator_run._time_created = trial.time_created generator_runs.append( self.generator_run_to_sqa( generator_run=status_quo_generator_run)) status_quo_name = trial.status_quo.name elif isinstance(trial, Trial): if trial.generator_run: generator_runs = [ self.generator_run_to_sqa( generator_run=trial.generator_run) ] # pyre-fixme: Expected `Base` for 1st...ot `typing.Type[Trial]`. trial_class: SQATrial = self.config.class_to_sqa_class[Trial] # pyre-fixme[29]: `SQATrial` is not a function. return trial_class( abandoned_reason=trial.abandoned_reason, deployed_name=trial.deployed_name, index=trial.index, is_batch=isinstance(trial, BatchTrial), num_arms_created=trial._num_arms_created, run_metadata=trial.run_metadata, status=trial.status, status_quo_name=status_quo_name, time_completed=trial.time_completed, time_created=trial.time_created, time_staged=trial.time_staged, time_run_started=trial.time_run_started, trial_type=trial.trial_type, abandoned_arms=abandoned_arms, generator_runs=generator_runs, runner=runner, )
def generator_run_from_sqa( self, generator_run_sqa: SQAGeneratorRun ) -> GeneratorRun: """Convert SQLAlchemy GeneratorRun to Ax GeneratorRun.""" arms = [] weights = [] opt_config = None search_space = None for arm_sqa in generator_run_sqa.arms: arms.append(self.arm_from_sqa(arm_sqa=arm_sqa)) weights.append(arm_sqa.weight) opt_config, tracking_metrics = self.opt_config_and_tracking_metrics_from_sqa( metrics_sqa=generator_run_sqa.metrics ) if len(tracking_metrics) > 0: raise SQADecodeError( # pragma: no cover "GeneratorRun should not have tracking metrics." ) search_space = self.search_space_from_sqa( parameters_sqa=generator_run_sqa.parameters, parameter_constraints_sqa=generator_run_sqa.parameter_constraints, ) best_arm_predictions = None model_predictions = None if ( generator_run_sqa.best_arm_parameters is not None and generator_run_sqa.best_arm_predictions is not None ): best_arm = Arm( name=generator_run_sqa.best_arm_name, # pyre-fixme[6]: Expected `Dict[str, Optional[Union[bool, float, # int, str]]]` for 2nd param but got `Optional[Dict[str, # Optional[Union[bool, float, int, str]]]]`. parameters=generator_run_sqa.best_arm_parameters, ) best_arm_predictions = ( best_arm, # pyre-fixme[6]: Expected `Iterable[_T_co]` for 1st param but got # `Optional[Tuple[Dict[str, float], Optional[Dict[str, Dict[str, # float]]]]]`. tuple(generator_run_sqa.best_arm_predictions), ) model_predictions = ( # pyre-fixme[6]: Expected `Iterable[_T_co]` for 1st param but got # `Optional[Tuple[Dict[str, List[float]], Dict[str, Dict[str, # List[float]]]]]`. tuple(generator_run_sqa.model_predictions) if generator_run_sqa.model_predictions is not None else None ) generator_run = GeneratorRun( arms=arms, weights=weights, optimization_config=opt_config, search_space=search_space, fit_time=generator_run_sqa.fit_time, gen_time=generator_run_sqa.gen_time, # pyre-fixme[6]: Expected `Optional[Tuple[Arm, Optional[Tuple[Dict[str, # float], Optional[Dict[str, Dict[str, float]]]]]]]` for 7th param but got # `Optional[Tuple[Arm, Tuple[Any, ...]]]`. best_arm_predictions=best_arm_predictions, model_predictions=model_predictions, model_key=generator_run_sqa.model_key, model_kwargs=object_from_json(generator_run_sqa.model_kwargs), bridge_kwargs=object_from_json(generator_run_sqa.bridge_kwargs), gen_metadata=object_from_json(generator_run_sqa.gen_metadata), model_state_after_gen=object_from_json( generator_run_sqa.model_state_after_gen ), generation_step_index=generator_run_sqa.generation_step_index, candidate_metadata_by_arm_signature=object_from_json( generator_run_sqa.candidate_metadata_by_arm_signature ), ) generator_run._time_created = generator_run_sqa.time_created generator_run._generator_run_type = self.get_enum_name( value=generator_run_sqa.generator_run_type, enum=self.config.generator_run_type_enum, ) generator_run._index = generator_run_sqa.index return generator_run
def trial_to_sqa(self, trial: BaseTrial, generator_run_reduced_state: bool = False) -> SQATrial: """Convert Ax Trial to SQLAlchemy. In addition to creating and storing a new Trial object, we need to create and store the GeneratorRuns and Runner that it owns. """ runner = None if trial.runner: runner = self.runner_to_sqa(runner=not_none(trial.runner)) abandoned_arms = [] generator_runs = [] status_quo_name = None optimize_for_power = None if isinstance(trial, Trial) and trial.generator_run: gr_sqa = self.generator_run_to_sqa( generator_run=not_none(trial.generator_run), reduced_state=generator_run_reduced_state, ) generator_runs.append(gr_sqa) elif isinstance(trial, BatchTrial): for abandoned_arm in trial.abandoned_arms_metadata: abandoned_arms.append( self.abandoned_arm_to_sqa(abandoned_arm=abandoned_arm)) for struct in trial.generator_run_structs: gr_sqa = self.generator_run_to_sqa( generator_run=struct.generator_run, weight=struct.weight) generator_runs.append(gr_sqa) trial_status_quo = trial.status_quo trial_status_quo_weight_override = trial._status_quo_weight_override if (trial_status_quo is not None and trial_status_quo_weight_override is not None): status_quo_generator_run = GeneratorRun( arms=[trial_status_quo], weights=[trial_status_quo_weight_override], type=GeneratorRunType.STATUS_QUO.name, ) # This is a hack necessary to get equality tests passing; # otherwise you can encode same object and get two different results. status_quo_generator_run._time_created = trial.time_created gr_sqa = self.generator_run_to_sqa( generator_run=status_quo_generator_run) # pyre-ignore Attribute `id` declared in class `SQAGeneratorRun` # has type `int` but is used as type `Optional[int]`. gr_sqa.id = trial._status_quo_generator_run_db_id # pyre-ignore Attribute `id` declared in class `SQAArm` # has type `int` but is used as type `Optional[int]`. gr_sqa.arms[0].id = trial._status_quo_arm_db_id generator_runs.append(gr_sqa) status_quo_name = trial_status_quo.name optimize_for_power = getattr(trial, "optimize_for_power", None) if optimize_for_power is None: logger.warning( f"optimize_for_power not present in BatchTrial: {trial.__dict__}" ) # pyre-ignore[9]: Expected `Base` for 1st...ot `typing.Type[Trial]`. trial_class: SQATrial = self.config.class_to_sqa_class[Trial] trial_sqa = trial_class( # pyre-fixme[29]: `SQATrial` is not a function. id=trial.db_id, abandoned_reason=trial.abandoned_reason, deployed_name=trial.deployed_name, index=trial.index, is_batch=isinstance(trial, BatchTrial), num_arms_created=trial._num_arms_created, optimize_for_power=optimize_for_power, ttl_seconds=trial.ttl_seconds, run_metadata=trial.run_metadata, stop_metadata=trial.stop_metadata, status=trial.status, status_quo_name=status_quo_name, time_completed=trial.time_completed, time_created=trial.time_created, time_staged=trial.time_staged, time_run_started=trial.time_run_started, trial_type=trial.trial_type, abandoned_arms=abandoned_arms, generator_runs=generator_runs, runner=runner, generation_step_index=trial._generation_step_index, properties=trial._properties, ) return trial_sqa
def generator_run_from_sqa( self, generator_run_sqa: SQAGeneratorRun, reduced_state: bool, immutable_search_space_and_opt_config: bool, ) -> GeneratorRun: """Convert SQLAlchemy GeneratorRun to Ax GeneratorRun. Args: generator_run_sqa: `SQAGeneratorRun` to decode. reduced_state: Whether to load generator runs with a slightly reduced state (without model state, search space, and optimization config). immutable_search_space_and_opt_config: Whether to load generator runs without search space and optimization config. Unlike `reduced_state`, we do still load model state. """ arms = [] weights = [] opt_config = None search_space = None for arm_sqa in generator_run_sqa.arms: arms.append(self.arm_from_sqa(arm_sqa=arm_sqa)) weights.append(arm_sqa.weight) if not reduced_state and not immutable_search_space_and_opt_config: ( opt_config, tracking_metrics, ) = self.opt_config_and_tracking_metrics_from_sqa( metrics_sqa=generator_run_sqa.metrics) if len(tracking_metrics) > 0: raise SQADecodeError( # pragma: no cover "GeneratorRun should not have tracking metrics.") search_space = self.search_space_from_sqa( parameters_sqa=generator_run_sqa.parameters, parameter_constraints_sqa=generator_run_sqa. parameter_constraints, ) best_arm_predictions = None model_predictions = None if (generator_run_sqa.best_arm_parameters is not None and generator_run_sqa.best_arm_predictions is not None): best_arm = Arm( name=generator_run_sqa.best_arm_name, parameters=not_none(generator_run_sqa.best_arm_parameters), ) best_arm_predictions = ( best_arm, tuple(not_none(generator_run_sqa.best_arm_predictions)), ) model_predictions = ( tuple(not_none(generator_run_sqa.model_predictions)) if generator_run_sqa.model_predictions is not None else None) generator_run = GeneratorRun( arms=arms, weights=weights, optimization_config=opt_config, search_space=search_space, fit_time=generator_run_sqa.fit_time, gen_time=generator_run_sqa.gen_time, best_arm_predictions=best_arm_predictions, # pyre-ignore[6] # pyre-fixme[6]: Expected `Optional[Tuple[typing.Dict[str, List[float]], # typing.Dict[str, typing.Dict[str, List[float]]]]]` for 8th param but got # `Optional[typing.Tuple[Union[typing.Dict[str, List[float]], # typing.Dict[str, typing.Dict[str, List[float]]]], ...]]`. model_predictions=model_predictions, model_key=generator_run_sqa.model_key, model_kwargs=None if reduced_state else object_from_json( generator_run_sqa.model_kwargs, decoder_registry=self.config.json_decoder_registry, class_decoder_registry=self.config.json_class_decoder_registry, ), bridge_kwargs=None if reduced_state else object_from_json( generator_run_sqa.bridge_kwargs, decoder_registry=self.config.json_decoder_registry, class_decoder_registry=self.config.json_class_decoder_registry, ), gen_metadata=None if reduced_state else object_from_json( generator_run_sqa.gen_metadata, decoder_registry=self.config.json_decoder_registry, class_decoder_registry=self.config.json_class_decoder_registry, ), model_state_after_gen=None if reduced_state else object_from_json( generator_run_sqa.model_state_after_gen, decoder_registry=self.config.json_decoder_registry, class_decoder_registry=self.config.json_class_decoder_registry, ), generation_step_index=generator_run_sqa.generation_step_index, candidate_metadata_by_arm_signature=object_from_json( generator_run_sqa.candidate_metadata_by_arm_signature, decoder_registry=self.config.json_decoder_registry, class_decoder_registry=self.config.json_class_decoder_registry, ), ) generator_run._time_created = generator_run_sqa.time_created generator_run._generator_run_type = self.get_enum_name( value=generator_run_sqa.generator_run_type, enum=self.config.generator_run_type_enum, ) generator_run._index = generator_run_sqa.index generator_run.db_id = generator_run_sqa.id return generator_run
def trial_to_sqa(self, trial: BaseTrial) -> Tuple[SQATrial, T_OBJ_TO_SQA]: """Convert Ax Trial to SQLAlchemy and compile a list of (object, sqa_counterpart) tuples to set `db_id` on user-facing classes after the conversion is complete and the SQL session is flushed (SQLAlchemy classes receive their `id` attributes during `session.flush()`). In addition to creating and storing a new Trial object, we need to create and store the GeneratorRuns and Runner that it owns. """ obj_to_sqa = [] runner = None if trial.runner: runner = self.runner_to_sqa(runner=not_none(trial.runner)) obj_to_sqa.append((trial.runner, runner)) abandoned_arms = [] generator_runs = [] status_quo_name = None optimize_for_power = None if isinstance(trial, Trial) and trial.generator_run: gr_sqa, _obj_to_sqa = self.generator_run_to_sqa( generator_run=not_none(trial.generator_run)) generator_runs.append(gr_sqa) obj_to_sqa.extend(_obj_to_sqa) elif isinstance(trial, BatchTrial): for abandoned_arm in trial.abandoned_arms_metadata: abandoned_arms.append( self.abandoned_arm_to_sqa(abandoned_arm=abandoned_arm)) for struct in trial.generator_run_structs: gr_sqa, _obj_to_sqa = self.generator_run_to_sqa( generator_run=struct.generator_run, weight=struct.weight) generator_runs.append(gr_sqa) obj_to_sqa.extend(_obj_to_sqa) trial_status_quo = trial.status_quo trial_status_quo_weight_override = trial._status_quo_weight_override if (trial_status_quo is not None and trial_status_quo_weight_override is not None): status_quo_generator_run = GeneratorRun( arms=[trial_status_quo], weights=[trial_status_quo_weight_override], type=GeneratorRunType.STATUS_QUO.name, ) # This is a hack necessary to get equality tests passing; # otherwise you can encode same object and get two different results. status_quo_generator_run._time_created = trial.time_created gr_sqa, _obj_to_sqa = self.generator_run_to_sqa( generator_run=status_quo_generator_run) obj_to_sqa.extend(_obj_to_sqa) generator_runs.append(gr_sqa) status_quo_name = trial_status_quo.name optimize_for_power = getattr(trial, "optimize_for_power", None) if optimize_for_power is None: logger.warning( f"optimize_for_power not present in BatchTrial: {trial.__dict__}" ) # pyre-ignore[9]: Expected `Base` for 1st...ot `typing.Type[Trial]`. trial_class: SQATrial = self.config.class_to_sqa_class[Trial] trial_sqa = trial_class( # pyre-fixme[29]: `SQATrial` is not a function. abandoned_reason=trial.abandoned_reason, deployed_name=trial.deployed_name, index=trial.index, is_batch=isinstance(trial, BatchTrial), num_arms_created=trial._num_arms_created, optimize_for_power=optimize_for_power, ttl_seconds=trial.ttl_seconds, run_metadata=trial.run_metadata, status=trial.status, status_quo_name=status_quo_name, time_completed=trial.time_completed, time_created=trial.time_created, time_staged=trial.time_staged, time_run_started=trial.time_run_started, trial_type=trial.trial_type, abandoned_arms=abandoned_arms, generator_runs=generator_runs, runner=runner, generation_step_index=trial._generation_step_index, properties=trial._properties, ) obj_to_sqa.append((trial, trial_sqa)) return trial_sqa, obj_to_sqa
def trial_to_sqa(self, trial: BaseTrial) -> SQATrial: """Convert Ax Trial to SQLAlchemy. In addition to creating and storing a new Trial object, we need to create and store the GeneratorRuns and Runner that it owns. """ # pyre-fixme[6]: Expected `Runner` for 1st param but got `Optional[Runner]`. runner = self.runner_to_sqa( runner=trial.runner) if trial.runner else None abandoned_arms = [] generator_runs = [] status_quo_name = None optimize_for_power = None if isinstance(trial, BatchTrial): abandoned_arms = [ self.abandoned_arm_to_sqa(abandoned_arm=abandoned_arm) for abandoned_arm in trial.abandoned_arms_metadata ] generator_runs = [ self.generator_run_to_sqa(generator_run=struct.generator_run, weight=struct.weight) for struct in trial.generator_run_structs ] # appease pyre trial_status_quo = trial.status_quo trial_status_quo_weight_override = trial._status_quo_weight_override if (trial_status_quo is not None and trial_status_quo_weight_override is not None): status_quo_generator_run = GeneratorRun( arms=[trial_status_quo], weights=[trial_status_quo_weight_override], type=GeneratorRunType.STATUS_QUO.name, ) # this is a hack necessary to get equality tests passing; # otherwise you can encode same object and get two different results status_quo_generator_run._time_created = trial.time_created generator_runs.append( self.generator_run_to_sqa( generator_run=status_quo_generator_run)) status_quo_name = trial_status_quo.name if hasattr(trial, "optimize_for_power"): optimize_for_power = trial.optimize_for_power else: optimize_for_power = None logger.warning( f"optimize_for_power not present in BatchTrial: {trial.__dict__}" ) elif isinstance(trial, Trial): if trial.generator_run: generator_runs = [ # pyre-fixme[6]: Expected `GeneratorRun` for 1st param but got # `Optional[GeneratorRun]`. self.generator_run_to_sqa(generator_run=trial.generator_run ) ] # pyre-fixme: Expected `Base` for 1st...ot `typing.Type[Trial]`. trial_class: SQATrial = self.config.class_to_sqa_class[Trial] # pyre-fixme[29]: `SQATrial` is not a function. return trial_class( abandoned_reason=trial.abandoned_reason, deployed_name=trial.deployed_name, index=trial.index, is_batch=isinstance(trial, BatchTrial), num_arms_created=trial._num_arms_created, optimize_for_power=optimize_for_power, ttl_seconds=trial.ttl_seconds, run_metadata=trial.run_metadata, status=trial.status, status_quo_name=status_quo_name, time_completed=trial.time_completed, time_created=trial.time_created, time_staged=trial.time_staged, time_run_started=trial.time_run_started, trial_type=trial.trial_type, abandoned_arms=abandoned_arms, generator_runs=generator_runs, runner=runner, generation_step_index=trial._generation_step_index, properties=trial._properties, )
def generator_run_from_sqa( self, generator_run_sqa: SQAGeneratorRun ) -> GeneratorRun: """Convert SQLAlchemy GeneratorRun to Ax GeneratorRun.""" arms = [] weights = [] opt_config = None search_space = None for arm_sqa in generator_run_sqa.arms: arms.append(self.arm_from_sqa(arm_sqa=arm_sqa)) weights.append(arm_sqa.weight) opt_config, tracking_metrics = self.opt_config_and_tracking_metrics_from_sqa( metrics_sqa=generator_run_sqa.metrics ) if len(tracking_metrics) > 0: raise SQADecodeError( # pragma: no cover "GeneratorRun should not have tracking metrics." ) search_space = self.search_space_from_sqa( parameters_sqa=generator_run_sqa.parameters, parameter_constraints_sqa=generator_run_sqa.parameter_constraints, ) best_arm_predictions = None model_predictions = None if ( generator_run_sqa.best_arm_parameters is not None and generator_run_sqa.best_arm_predictions is not None ): best_arm = Arm( name=generator_run_sqa.best_arm_name, parameters=generator_run_sqa.best_arm_parameters, ) best_arm_predictions = ( best_arm, tuple(generator_run_sqa.best_arm_predictions), ) model_predictions = ( tuple(generator_run_sqa.model_predictions) if generator_run_sqa.model_predictions is not None else None ) generator_run = GeneratorRun( arms=arms, weights=weights, optimization_config=opt_config, search_space=search_space, fit_time=generator_run_sqa.fit_time, gen_time=generator_run_sqa.gen_time, best_arm_predictions=best_arm_predictions, model_predictions=model_predictions, model_key=generator_run_sqa.model_key, model_kwargs=object_from_json(generator_run_sqa.model_kwargs), bridge_kwargs=object_from_json(generator_run_sqa.bridge_kwargs), ) generator_run._time_created = generator_run_sqa.time_created generator_run._generator_run_type = self.get_enum_name( value=generator_run_sqa.generator_run_type, enum=self.config.generator_run_type_enum, ) generator_run._index = generator_run_sqa.index return generator_run