Goals

Goals are described in the class rtctools.optimization.goal_programming_mixin.Goal.

class rtctools.optimization.goal_programming_mixin.Goal[source]

Bases: object

Base class for lexicographic goal programming goals.

Types of goals

There are 2 types of goals: minimization goals and target goals.

Minimization goals

Minimization goals are of the form:

\[\text{minimize } f(x).\]

Target goals

Target goals weakly enforce a constraint of the form

\[m_{target} \leq g(x) \leq M_{target},\]

by turning it into a minimization problem of the form

\[\begin{split}\text{minimize } & \epsilon^r, \\ \text{subject to } &g_{low}(\epsilon) \leq g(x) \leq g_{up}(\epsilon), \\ \text{and } &0 \leq \epsilon \leq 1,\end{split}\]

where

\[\begin{split}g_{low}(\epsilon) &:= (1-\epsilon) m_{target} + \epsilon m, \\ g_{up}(\epsilon) &:= (1-\epsilon) M_{target} + \epsilon M.\end{split}\]

Here, \(m\) and \(M\) are hard constraints for \(g(x)\), \(m_{target}\) and \(M_{target}\) are target bounds for \(g(x)\), \(\epsilon\) is an auxiliary variable that indicates how strongly the target bounds are violated, and \(\epsilon^r\) is a function that indicates the variation of \(\epsilon\), where the order \(r\) is by default \(2\). We have

\[m < m_{target} \leq M_{target} < M.\]

Note that when \(\epsilon=0\), the constraint on \(g(x)\) becomes

\[m_{target} \leq g(x) \leq M_{target}\]

and if \(\epsilon=1\), it becomes

\[m \leq g(x) \leq M.\]

Scaling goals

Goals can be scaled by a nominal value \(c_{nom}\) to improve the performance of the solvers. In case of a minimization goal, the scaled problem is given by

\[\text{minimize } \hat{f}(x),\]

where \(\hat{f}(x) := f(x) / c_{nom}\). In case of a target goal, the scaled problem is given by

\[\begin{split}\text{minimize } & \epsilon^r, \\ \text{subject to } &\hat{g}_{low}(\epsilon) \leq \hat{g}(x) \leq \hat{g}_{up}(\epsilon), \\ \text{and } &0 \leq \epsilon \leq 1,\end{split}\]

where \(\hat{g}(x) := g(x) / c_{nom}\), \(\hat{g}_{low}(\epsilon) := {g}_{low}(\epsilon) / c_{nom}\), and \(\hat{g}_{up}(\epsilon) := {g}_{up}(\epsilon) / c_{nom}\).

Implementing goals

A goal class is created by inheriting from the :py:class:Goal class and overriding the function() method. This method defines the goal function \(f(x)\) in case of a minimization goal, and the goal function \(g(x)\) in case of a target goal. A goal becomes a target goal if either the class attribute target_min or target_max is set.

To further define a goal, the following class attributes can also be set.

Variables:
  • function_range – Range of goal function \([m ,M]\). Only applies to target goals. Required for a target goal.

  • function_nominal – Nominal value of a function \(c_{nom}\). Used for scaling. Default is 1.

  • target_min – Desired lower bound for goal function \(m_{target}\). Default is numpy.nan.

  • target_max – Desired upper bound for goal function \(M_{target}\). Default is numpy.nan.

  • priority – Priority of a goal. Default is 1.

  • weight – Optional weighting applied to the goal. Default is 1.0.

  • order – Penalization order of goal violation \(r\). Default is 2.

  • critical – If True, the algorithm will abort if this goal cannot be fully met. Default is False.

  • relaxation – Amount of slack added to the hard constraints related to the goal. Must be a nonnegative value. The unit is equal to that of the goal function. Default is 0.0.

When target_min is set, but not target_max, the target goal becomes a lower bound target goal and the constraint on \(g(x)\) becomes

\[g_{low}(\epsilon) \leq g(x).\]

Similary, if target_max is set, but not target_min, the target goal becomes a upper bound target goal and the constraint on \(g(x)\) becomes

\[g(x) \leq g_{up}(\epsilon).\]

Relaxation is used to loosen the constraints that are set after the optimization of the goal’s priority.

Notes:
  • If one is unsure about the function range, it is recommended to overestimate this interval. However, this will negatively influence how accurately the target bounds are met.

  • The function range should be strictly larger than the target range. In particular, \(m < m_{target}\) and \(M_{target} < M\).

  • In a path goal, the target can be a Timeseries.

  • In case of multiple goals with the same priority, it is crucial that an accurate function nominal value is provided. This ensures that all goals are given similar importance.

A goal can be written in vector form. In a vector goal:
  • The goal size determines how many goals there are.

  • The goal function has shape (goal size, 1).

  • The function is either minimized or has, possibly various, targets.

  • Function nominal can either be an array with as many entries as the goal size or have a single value.

  • Function ranges can either be an array with as many entries as the goal size or have a single value.

  • In a goal, the target can either be an array with as many entries as the goal size or have a single value.

  • In a path goal, the target can also be a Timeseries whose values are either a 1-dimensional vector or have as many columns as the goal size.

Examples

Example definition of the point goal \(x(t) \geq 1.1\) for \(t=1.0\) at priority 1:

class MyGoal(Goal):
    def function(self, optimization_problem, ensemble_member):
        # State 'x' at time t = 1.0
        t = 1.0
        return optimization_problem.state_at('x', t, ensemble_member)

    function_range = (1.0, 2.0)
    target_min = 1.1
    priority = 1

Example definition of the path goal \(x(t) \geq 1.1\) for all \(t\) at priority 2:

class MyPathGoal(Goal):
    def function(self, optimization_problem, ensemble_member):
        # State 'x' at any point in time
        return optimization_problem.state('x')

    function_range = (1.0, 2.0)
    target_min = 1.1
    priority = 2

Note path goals

Note that for path goals, the ensemble member index is not passed to the call to OptimizationProblem.state(). This call returns a time-independent symbol that is also independent of the active ensemble member. Path goals are applied to all times and all ensemble members simultaneously.

critical = False

Critical goals must always be fully satisfied.

abstract function(optimization_problem: OptimizationProblem, ensemble_member: int) MX[source]

This method returns a CasADi MX object describing the goal function.

Returns:

A CasADi MX object.

function_nominal = 1.0

Nominal value of function (used for scaling)

function_range = (nan, nan)

Range of goal function

function_value_timeseries_id = None

Timeseries ID for function value data (optional)

get_function_key(optimization_problem: OptimizationProblem, ensemble_member: int) str[source]

Returns a key string uniquely identifying the goal function. This is used to eliminate linearly dependent constraints from the optimization problem.

property has_target_bounds: bool

True if the user goal has min/max bounds.

property has_target_max: bool

True if the user goal has max bounds.

property has_target_min: bool

True if the user goal has min bounds.

order = 2

The goal violation value is taken to the order’th power in the objective function.

priority = 1

Lower priority goals take precedence over higher priority goals.

relaxation = 0.0

Absolute relaxation applied to the optimized values of this goal

size = 1

The size of the goal if it’s a vector goal.

target_max = nan

Desired upper bound for goal function

target_min = nan

Desired lower bound for goal function

violation_timeseries_id = None

Timeseries ID for goal violation data (optional)

weight = 1.0

Goals with the same priority are weighted off against each other in a single objective function.

class rtctools.optimization.goal_programming_mixin.StateGoal(optimization_problem)[source]

Bases: Goal

Base class for lexicographic goal programming path goals that act on a single model state.

A state goal is defined by setting at least the state class variable.

Variables:
  • state – State on which the goal acts. Required.

  • target_min – Desired lower bound for goal function. Default is numpy.nan.

  • target_max – Desired upper bound for goal function. Default is numpy.nan.

  • priority – Integer priority of goal. Default is 1.

  • weight – Optional weighting applied to the goal. Default is 1.0.

  • order – Penalization order of goal violation. Default is 2.

  • critical – If True, the algorithm will abort if this goal cannot be fully met. Default is False.

Example definition of the goal \(x(t) \geq 1.1\) for all \(t\) at priority 2:

class MyStateGoal(StateGoal):
    state = 'x'
    target_min = 1.1
    priority = 2

Contrary to ordinary Goal objects, PathGoal objects need to be initialized with an OptimizationProblem instance to allow extraction of state metadata, such as bounds and nominal values. Consequently, state goals must be instantiated as follows:

my_state_goal = MyStateGoal(optimization_problem)

Note that StateGoal is a helper class. State goals can also be defined using Goal as direct base class, by implementing the function method and providing the function_range and function_nominal class variables manually.

__init__(optimization_problem)[source]

Initialize the state goal object.

Parameters:

optimization_problemOptimizationProblem instance.