Source code for desdeo_problem.problem.Variable

"""Desdeo-problem variable related definitions.

This file includes definition of Variable class (with initial and current
values and value bounds), variable_builder, and error classes for Variable
and VariableBuilder.
"""

from os import path
from typing import List, Tuple, Union

import numpy as np


[docs]class VariableError(Exception): """Raised when an error is encountered during the handling of the Variable objects. """ pass
[docs]class VariableBuilderError(Exception): """Raised when an error is encountered during the handling of the Variable objects. """ pass
[docs]class Variable: """Simple variable with a name, initial value and bounds. Arguments: name (str): Name of the variable initial_value (float): The initial value of the variable. lower_bound (float, optional): Lower bound of the variable. Defaults to negative infinity. upper_bound (float, optional): Upper bound of the variable. Defaults to positive infinity. Attributes: name (str): Name of the variable. initial_value (float): Initial value of the variable. lower_bound (float): Lower bound of the variable. upper_bound (float): Upper bound of the variable. current_value (float): The current value the variable holds. Raises: VariableError: Bounds are incorrect. """ def __init__( self, name: str, initial_value: float, lower_bound: float = -np.inf, upper_bound: float = np.inf, ) -> None: self.__name: str = name self.__initial_value: float self.__lower_bound: float self.__upper_bound: float self.__current_value: float # NOTE: This is probably a useless attr # Check that the bounds make sense if not (lower_bound < upper_bound): msg = ("Lower bound {} should be less than the upper bound " "{}.").format(lower_bound, upper_bound) raise VariableError(msg) # Check that the initial value is between the bounds if not (lower_bound <= initial_value <= upper_bound): msg = "The initial value {} should be between the " "upper ({}) and lower ({}) bounds.".format( initial_value, lower_bound, upper_bound ) raise VariableError(msg) self.__lower_bound = lower_bound self.__upper_bound = upper_bound self.__initial_value = initial_value self.__current_value = initial_value @property def name(self) -> str: """Property: name Returns: str: The name of the variable. """ return self.__name @property def initial_value(self) -> float: """Property: initial_value Returns: float: The initial value of the variable. """ return self.__initial_value @property def current_value(self) -> float: """Property: current_value Returns: float: The current value of the variable """ return self.__current_value @current_value.setter def current_value(self, value: float): """Setter: current_value Args: float: The updated value for the current_value variable. """ self.__current_value = value
[docs] def get_bounds(self) -> Tuple[float, float]: """Return the bounds of the variables as a tuple. Returns: tuple(float, float): A tuple consisting of (lower_bound, upper_bound) """ return (self.__lower_bound, self.__upper_bound)
[docs]def variable_builder( names: List[str], initial_values: Union[List[float], np.ndarray], lower_bounds: Union[List[float], np.ndarray] = None, upper_bounds: Union[List[float], np.ndarray] = None, ) -> List[Variable]: """Automatically build all variable objects. Arguments: names (List[str]): Names of the variables initial_values (np.ndarray): Initial values taken by the variables. lower_bounds (Union[List[float], np.ndarray], optional): Lower bounds of the variables. If None, it defaults to negative infinity. Defaults to None. upper_bounds (Union[List[float], np.ndarray], optional): Upper bounds of the variables. If None, it defaults to positive infinity. Defaults to None. Raises: VariableError: Lengths of the input arrays are different. Returns: List[Variable]: List of variable objects """ # assert that all inputs have the same size num_of_variables = len(names) if initial_values is None: initial_values = [None] * num_of_variables if not (num_of_variables == len(initial_values)): msg = ( "The length of the list of names and the number of elements in the " "initial_values array should be the same" ) raise VariableBuilderError(msg) if lower_bounds is None: lower_bounds = [-np.inf] * num_of_variables if not (num_of_variables == len(lower_bounds)): msg = ( "The length of the list of names and the number of elements in the " "lower_bounds array should be the same" ) raise VariableBuilderError(msg) if upper_bounds is None: upper_bounds = [np.inf] * num_of_variables if not (num_of_variables == len(upper_bounds)): msg = ( "The length of the list of names and the number of elements in the " "upper_bounds array should be the same" ) raise VariableBuilderError(msg) # if most checks passed variables = [Variable(*var_data) for var_data in zip(names, initial_values, lower_bounds, upper_bounds)] return variables