Source code for gemseo.scenarios.scenario_results.bilevel_scenario_result

# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""BiLevel scenario result."""

from __future__ import annotations

from typing import TYPE_CHECKING
from typing import Final

from gemseo.algos.opt_result import OptimizationResult
from gemseo.scenarios.scenario_results.scenario_result import ScenarioResult

if TYPE_CHECKING:
    from pathlib import Path

    from gemseo.core.scenario import Scenario


[docs] class BiLevelScenarioResult(ScenarioResult): """The result of a :class:`.Scenario` using a :class:`.BiLevel` formulation.""" __SUB_LABEL_FORMATTER: Final[str] = "sub_{}" """The formatter to get the name of the key of a sub-problem from its index. To be used as ``__SUB_LABEL_FORMATTER.format(i)`` where ``i`` is an integer. """ __n_sub_problems: int """The number of sub-optimization problems.""" def __init__(self, scenario: Scenario | str | Path) -> None: # noqa: D107 super().__init__(scenario) formulation = scenario.formulation main_problem = formulation.opt_problem x_shared_opt = main_problem.solution.x_opt i_opt = main_problem.database.get_iteration(x_shared_opt) - 1 scenario_adapters = formulation.scenario_adapters self.__n_sub_problems = len(scenario_adapters) for index, scenario_adapter in enumerate(scenario_adapters): sub_problem = scenario_adapter.scenario.formulation.opt_problem database = sub_problem.database sub_problem.database = scenario_adapter.databases[i_opt] result = OptimizationResult.from_optimization_problem(sub_problem) x_local_opt = result.x_opt label = self.__SUB_LABEL_FORMATTER.format(index) self.optimization_problems_to_results[label] = result self.design_variable_names_to_values.update( sub_problem.design_space.array_to_dict(x_local_opt) ) sub_problem.database = database
[docs] def get_top_optimization_result(self) -> OptimizationResult: """Return the optimization result of the top-level optimization problem.""" return self.optimization_result
[docs] def get_sub_optimization_result(self, index: int) -> OptimizationResult | None: """Return the optimization result of a sub-optimization problem if any. Args: index: The index of the sub-optimization problem, between 0 and N-1 where N is the number of sub-optimization problems. Returns: The optimization result of a sub-optimization problem. Raises: ValueError: If the index is greater than N-1. """ max_index = self.__n_sub_problems - 1 if index > max_index: msg = ( f"The index ({index}) of a sub-optimization result " f"must be between 0 and {max_index}." ) raise ValueError(msg) return self.optimization_problems_to_results[ self.__SUB_LABEL_FORMATTER.format(index) ]