Source code for pymoo.core.variable

"""
Module containing infrastructure for representing decision variable classes.
"""

# public API for when using ``from pymoo.core.variable import *``
__all__ = [
    "Variable",
    "BoundedVariable",
    "Real",
    "Integer",
    "Binary",
    "Choice",
    "get",
]

from typing import Any, Optional, Tuple
from typing import Union
import numpy as np
from numpy.typing import ArrayLike
from pymoo.util import default_random_state


class Variable(object):
    """
    Semi-abstract base class for the representation of a decision variable.
    """

    def __init__(
            self, 
            value: Optional[object] = None, 
            active: bool = True, 
            flag: str = "default",
        ) -> None:
        """
        Constructor for the ``Variable`` class.

        Parameters
        ----------
        value : object, None
            Value the decision variable is to take.
        active : bool
            Whether the variable is active (``True``) or inactive (``False``).
        flag : str
            Flag to bind to the decision variable.
        """
        super().__init__()
        self.value = value
        self.flag = flag
        self.active = active

    @default_random_state
    def sample(
            self, 
            n: Optional[int] = None,
            random_state=None,
        ) -> Union[object,np.ndarray]:
        """
        Randomly sample ``n`` instances of a decision variable.

        Parameters
        ----------
        n : int, None
            Number of decision variable samples which to draw.
            If ``int``, sample ``n`` decision variables.
            If ``None``, sample a single decision variables.
        
        Returns
        -------
        out : object, np.ndarray
            If ``n`` is ``int``, return a ``np.ndarray`` of shape ``(n,)`` 
            containing sampled decision variables.
            If ``n`` is ``None``, return an ``object`` of a sampled decision 
            variable.
        """
        if n is None:
            return self._sample(1, random_state=random_state)[0]
        else:
            return self._sample(n, random_state=random_state)

    def _sample(
            self, 
            n: int,
            random_state=None,
        ) -> np.ndarray:
        """
        Randomly sample ``n`` instances of a decision variable.
        This is an abstract private method governing the behavior of the 
        ``sample`` method.

        Parameters
        ----------
        n : int
            Number of decision variable samples which to draw.

        Returns
        -------
        out : np.ndarray
            An array of shape ``(n,)`` containing sampled decision variables.
        """
        pass

    def set(
            self, 
            value: object,
        ) -> None:
        """
        Set the value of a decision variable.

        Parameters
        ----------
        value : object
            Value to assign to the decision variable.
        """
        self.value = value

    def get(
            self, 
            **kwargs: Any
        ) -> object:
        """
        Get the value of a decision variable.

        Parameters
        ----------
        kwargs : Any
            Additional keyword arguments.
        
        Returns
        -------
        out : object
            The value of the decision variable.
        """
        return self.value


class BoundedVariable(Variable):
    """
    Semi-abstract class for the representation of a bounded decision variable.
    """

    def __init__(
            self, 
            value: Optional[object] = None, 
            bounds: Tuple[Optional[object],Optional[object]] = (None, None), 
            strict: Optional[Tuple[Optional[object],Optional[object]]] = None, 
            **kwargs: Any,
        ) -> None:
        """
        Constructor for the ``BoundedVariable`` class.

        Parameters
        ----------
        value : object
            Value the decision variable is to take.
        bounds : tuple
            A tuple of length 2 containing upper and lower limits for the 
            decision variable.
        strict : tuple, None
            Strict boundaries for the decision variable.
            If ``None``, the value of ``bounds`` is copied to ``strict``.
        kwargs : Any
            Additional keyword arguments for ``active`` and ``flag``.
        """
        # call the Variable constructor 
        super().__init__(value=value, **kwargs)
        self.bounds = bounds

        # if no strict boundaries were provided, consider ``bounds`` as 
        # strict boundaries
        if strict is None:
            strict = bounds
        self.strict = strict

    @property
    def lb(self) -> object:
        """
        Lower bound of the decision variable.

        Returns
        -------
        out : object
            The decision variable lower bound.
        """
        return self.bounds[0]

    @property
    def ub(self) -> object:
        """
        Upper bound of the decision variable.

        Returns
        -------
        out : object
            The decision variable upper bound.
        """
        return self.bounds[1]


[docs] class Real(BoundedVariable): """ Class for the representation of bounded, real decision variables. """ # variable type represented by this object class vtype = float def _sample( self, n: int, random_state=None, ) -> np.ndarray: """ Randomly sample ``n`` instances of a real, bounded decision variable. Decision variables are sampled from a uniform distribution. This is a private method governing the behavior of the ``sample`` method. Parameters ---------- n : int Number of decision variable samples which to draw. random_state Random state for sampling. Returns ------- out : np.ndarray An array of shape ``(n,)`` containing sampled real, bounded decision variables. """ low, high = self.bounds return random_state.uniform(low=low, high=high, size=n)
[docs] class Integer(BoundedVariable): """ Class for the representation of bounded, integer decision variables. """ # variable type represented by this object class vtype = int def _sample( self, n: int, random_state=None, ) -> np.ndarray: """ Randomly sample ``n`` instances of a bounded, integer decision variable. Decision variables are sampled from a uniform distribution. This is a private method governing the behavior of the ``sample`` method. Parameters ---------- n : int Number of decision variable samples which to draw. random_state Random state for sampling. Returns ------- out : np.ndarray An array of shape ``(n,)`` containing sampled bounded, integer decision variables. """ low, high = self.bounds return random_state.integers(low, high + 1, size=n)
class Binary(BoundedVariable): """ Class for the representation of a binary, bounded decision variable. """ # variable type represented by this object class vtype = bool def _sample( self, n: int, random_state=None, ) -> np.ndarray: """ Randomly sample ``n`` instances of a bounded, binary decision variable. Decision variables are sampled from a uniform distribution. This is a private method governing the behavior of the ``sample`` method. Parameters ---------- n : int Number of decision variable samples which to draw. random_state Random state for sampling. Returns ------- out : np.ndarray An array of shape ``(n,)`` containing sampled bounded, binary decision variables. """ return random_state.random(size=n) < 0.5
[docs] class Choice(Variable): """ Class for the representation of a discrete, subset decision variable. """ # variable type represented by this object class vtype = object def __init__( self, value: Optional[object] = None, options: Optional[ArrayLike] = None, all: Optional[ArrayLike] = None, **kwargs: Any, ) -> None: """ Constructor for the ``Choice`` class. Parameters ---------- value : object Value the decision variable is to take. options : ArrayLike, None A list of decision variable options from which to choose. all : ArrayLike, None A strict list of decision variable options from which to choose. If ``None``, the value of ``options`` is copied to ``all``. kwargs : Any Additional keyword arguments for ``active`` and ``flag``. """ # all super constructor super().__init__(value=value, **kwargs) self.options = options # if strict list not provided, set to ``options`` if all is None: all = options self.all = all def _sample( self, n: int, random_state=None, ) -> np.ndarray: """ Randomly sample ``n`` instances of a discrete, subset decision variable. Decision variables are sampled with replacement from a uniform distribution. This is a private method governing the behavior of the ``sample`` method. Parameters ---------- n : int Number of decision variable samples which to draw. random_state Random state for sampling. Returns ------- out : np.ndarray An array of shape ``(n,)`` containing sampled bounded, integer decision variables. """ return random_state.choice(self.options, size=n)
def get( *args: Tuple[Union[Variable,object],...], size: Optional[Union[tuple,int]] = None, **kwargs: Any ) -> Union[tuple,object,None]: """ Get decision variable values from a tuple of ``Variable`` objects. Parameters ---------- args : tuple A tuple of ``Variable`` or ``object``s. size : tuple, int, None Size to reshape decision variables. kwargs : Any Additional keyword arguments to pass to the ``get`` method of the ``Variable`` class when getting decision variable values. Returns ------- out : tuple, object, None Decision variable value(s). """ if len(args) == 0: return ret = [] for arg in args: v = arg.get(**kwargs) if isinstance(arg, Variable) else arg if size is not None: if isinstance(v, np.ndarray): v = np.reshape(v, size) else: v = np.full(size, v) ret.append(v) if len(ret) == 1: return ret[0] else: return tuple(ret)