Source code for gemseo.algos.opt_result

# -*- coding: utf-8 -*-
# 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.

# Contributors:
#    INITIAL AUTHORS - initial API and implementation and/or initial
#                         documentation
#        :author: Francois Gallard
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
from __future__ import division, unicode_literals

import logging

from gemseo.utils.string_tools import MultiLineString

"""
Optimization result
*******************
"""


LOGGER = logging.getLogger(__name__)


[docs]class OptimizationResult(object): """Stores optimization results.""" DICT_REPR_ATTR = [ "x_0", "x_opt", "optimizer_name", "message", "f_opt", "status", "n_obj_call", "n_grad_call", "n_constr_call", "is_feasible", ] HDF_CSTR_GRAD_KEY = "constr_grad:" HDF_CSTR_KEY = "constr:" def __init__( self, x_0=None, x_opt=None, f_opt=None, status=None, constraints_values=None, constraints_grad=None, optimizer_name=None, message=None, n_obj_call=None, n_grad_call=None, n_constr_call=None, is_feasible=False, ): """Initialize optimization results. :param x_0: initial guess for design variables :param x_opt: optimal design variables values :param f_opt: the objective function values at optimum :param status: the optimizer status :param message: the optimizer message :param n_obj_call: number of call to objective function by optimizer :param n_grad_call: number of call to gradient function by optimizer :param n_constr_call: number of call to constraints function by optimizer :param is_feasible: True if the solution is feasible, false else """ self.x_0 = x_0 self.optimizer_name = optimizer_name self.x_opt = x_opt self.message = message self.f_opt = f_opt self.constraints_values = constraints_values self.constraints_grad = constraints_grad self.status = status self.n_obj_call = n_obj_call self.n_grad_call = n_grad_call self.n_constr_call = n_constr_call self.is_feasible = is_feasible def __repr__(self): msg = MultiLineString() msg.add("Optimization result:") msg.indent() msg.add("Design variables: {}", self.x_opt) msg.add("Objective function: {}", self.f_opt) msg.add("Feasible solution: {}", self.is_feasible) return str(msg) def __str__(self): msg = MultiLineString() msg.add("Optimization result:") msg.add("Objective value = {}", self.f_opt) if self.is_feasible: msg.add("The result is feasible.") else: msg.add("The result is not feasible.") msg.add("Status: {}", self.status) msg.add("Optimizer message: {}", self.message) if self.n_obj_call is not None: msg.add( "Number of calls to the objective function by the optimizer: {}", self.n_obj_call, ) if self.constraints_values and len(self.constraints_values) < 20: msg.add("Constraints values w.r.t. 0: ") msg.indent() for c_name in sorted(self.constraints_values.keys()): if c_name.startswith("-"): name = str(c_name[1:]) values = str(-self.constraints_values[c_name]) else: name = str(c_name) values = str(self.constraints_values[c_name]) msg.add("{} = {}", name, values) return str(msg)
[docs] def get_data_dict_repr(self): """Returns a dict representation of self for serialization functions are removed. :returns: a dict with attributes names as keys """ repr_dict = {} for attr_name in self.DICT_REPR_ATTR: attr = getattr(self, attr_name) if attr is not None: repr_dict[attr_name] = attr cgrad_key = OptimizationResult.HDF_CSTR_GRAD_KEY c_key = OptimizationResult.HDF_CSTR_KEY for cstr, cval in self.constraints_values.items(): repr_dict[c_key + cstr] = cval for cstr, cgrad in self.constraints_grad.items(): repr_dict[cgrad_key + cstr] = cgrad return repr_dict
[docs] @staticmethod def init_from_dict_repr(**kwargs): """Initializes a new opt result from a data dict typically used for deserialization. :param kwargs: key value pairs from DICT_REPR_ATTR """ allowed = OptimizationResult.DICT_REPR_ATTR filt_args = {k: v for k, v in kwargs.items() if k in allowed} non_allowed = {k: v for k, v in kwargs.items() if k not in filt_args} constraints_values = {} constraints_grad = {} cgrad_key = OptimizationResult.HDF_CSTR_GRAD_KEY c_key = OptimizationResult.HDF_CSTR_KEY for attr, val in non_allowed.items(): if attr.startswith(cgrad_key): key_clean = attr.replace(cgrad_key, "") constraints_grad[key_clean] = val elif attr.startswith(c_key): key_clean = attr.replace(c_key, "") constraints_values[key_clean] = val else: raise ValueError("Unknown attribute : " + str(attr)) opt_res = OptimizationResult( constraints_values=constraints_values, constraints_grad=constraints_grad, **filt_args ) return opt_res