Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

constraint

The constraint object is a nested list of sum objects. Thus, the constraint object is and contains sum objects. Fundamentally, a sum is defined much like a for loop in many programming languages. It has one or more variables that are being iterated over, start and end conditions, and something that goes on in the body of the loop. In this case, what goes on inside the body of the loop is defined by the exprMain field containing an expression.

When the sum is executed, child sums are executed before the exprMain of the parent. The result of exprMain is added to the overall solution score or collected in a variable for use by the parent, signaled by the use of resultVar in the child sum.

In the example below, the parent sum is iterating over dimension T (time) and state (dimension SZ in this case). For each row of the set, a child sum is executed with the resultVar of sum_shifts. This resultVar is then used in the exprMain of the parent.

If resultVar was not specified, then the child sum's exprMain would be added to the solution score, but since in this case it is specified, only the parent's exprMain is added to the solution score. Details of all constraint fields and the different type of iterators are given in more detail below.

Example:

  {
    "constraint": {
      "CID": "O3",
      "enabled": true,
      "type": "objective",
      "comment": "Objective: Cover Requirements",
      "sumIter": "iterDim",
      "iterDim": "T",
      "iterVars": [ "t" ],
      "sums": [
        {
          "sumIter": "iterDim",
          "iterDim": "SZ",
          "iterVars": [ "s" ],
          "exprMain": "EQP(sum_shifts, cover_array(t, s, req), cover_array(t, s, wu), cover_array(t, s, wo))",
          "sums": [
            {
              "sumIter": "iterDim",
              "iterDim": "R",
              "iterVars": [ "r" ],
              "exprMain": "A(r, t) = s",
              "resultVar": "sum_shifts"
            }
          ]
        }
      ]
    }
  }

Root Fields:

  • CID: Required. This is the constraint ID and must be unique within the PD. This CID is used for reporting purposes and in result data.
  • type: (Optional). Can be one of [hard|soft|objective] but is not required. It is primarily for reporting and analysis purposes.
    • hard: This denotes that the constraint must be satisfied (in that it is minimized to zero) in order for the solution to be considered feasible.
    • soft: The constraint is not required to be satisfied for a solution to be considered feasible.
    • objective: The constraint is or is a part of the objective function.
  • comment: (Optional). Purely descriptive information.
  • enabled: (Optional). Can be one of [true|false]. Enables or disabled a constraint. All are enabled by default. This can be used for testing or debugging.

Fields for root and all child sums:

  • sumIter: Required. Can be one of [iterArray|iterVar|iterDim]. Each of these types has their own related fields, and are described in their own section below.
    • iterArray: The sum will iterate over rows of a given 2D array, with columns of that array named in order by the iterVars. See below for this sum type's specific fields.
    • iterDim: The sum will iterate over either the R, T, or S dimensions of the atom array. See below for this sum type's specific fields.
    • iterVar: The sum will iterate over a start and end condition (expression). See below for this sum type's specific fields.
  • sums: (Optional). Array of child sums.

Fields if sumIter = iterVar:

  • iterVars: Required. Names the variable that is being iterated over and available to expressions.
  • exprFrom: Required. The expression that defines the starting value of the variable (defined in iterVars). This can be as simple as 0 or a complex expression such as (1 + T) / 2.
  • exprTo: Required. The expression that defines the ending value of the variable. Depending upon exprToEq, the sum will iterate either to one less than this value, or this exact value.
  • exprToEq: (Optional). One of [true|false]. Sets whether the exprTo is equal to or less than. By default, false.

Example:

  {
    "constraint": {
      "CID": "O1",
      "sumIter": "iterVar",
      "exprFrom": "0",
      "exprTo": "T - 1",
      "iterVars": [ "t" ],
      "exprMain": "cost(At(t), At(t + 1))"
    }
  }

Fields if sumIter = iterDim:

  • iterVars: Required. Names the variable that is being iterated over and available to expressions.
  • iterDim: Required. One of [R|T||S|SZ]
    • R: Iterate over the R (resource count) dimension of the atom array.
    • T: Iterate over the T (time count) dimension of the atom array.
    • S: Iterate over the S (state count) dimension of the atom array.
    • SZ: Same as S but starts at 1 instead of 0. This is for cases where the 0th state represents an empty atom - a zero state.

Example:

  {
    "constraint": {
      "CID": "C9",
      "comment": "Constraint #9 - Days Off",
      "penaltyVar": "hardPenalty",
      "sumIter": "iterDim",
      "iterDim": "R",
      "iterVars": [ "r" ],
      "sums": [
        {
          "sumIter": "iterDim",
          "iterDim": "T",
          "iterVars": [ "t" ],
          "exprMain": "ANY(r, t) * days_off(r, t)"
        }
      ]
    }
  }

Fields if sumIter = iterArray:

  • arrayName: Required. The name of the array to iterate over.
  • iterVars: Required. Names the columns as variables coming from the set. So, the first variable is the first column in the set. In the example below, r, t, s, w would correspond to the first four columns of the set.

Example:

  {
    "constraint": {
      "CID": "C2",
      "comment": "Constraint #2 - Origin and destination must be different",
      "penaltyVar": "hardPenalty",
      "sumIter": "iterArray",
      "arrayName": "phi",
      "iterVars": [
        "r",
        "s"
      ],
      "sums": [
        {
          "sumIter": "iterDim",
          "iterDim": "S",
          "iterVars": [
            "i"
          ],
          "exprMain": "Xr(r, i) * Xr(s, i)"
        }
      ]
    }
  }

iterVar vs. iterDim?

You might be wondering what the difference is between:

  {
    "constraint": {
      "sumIter": "iterVar",
      "exprFrom": "0",
      "exprTo": "T",
      "iterVars": [ "t" ]
    }
  }

...and...

  {
    "constraint": {
      "sumIter": "iterDim",
      "iterDim": "T",
      "iterVars": [ "t" ]
    }
  }

The answer would be theoretically nothing other than perhaps easier readability and less opportunity to make a typo. However, there are two very important reasons to use iterDim instead of iterVar when iterating over atom array dimensions:

  1. The solver does not have to evalute the end expression on each loop iteration - this can make a big difference in tight loops, especially when running in debug mode.
  2. When iterDim is used in the root constraint's sum, the solver can optimize the recalculation of the solution score by only recalculating portions of constraints that have changed.

Thus, in short, use iterDim for any and all complete atom array dimension sum's.