Exemple #1
0
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
Exemple #2
0
    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
Exemple #3
0
    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,
        )
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
    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,
        )
Exemple #9
0
    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