Special Object Types



The following special types are provided for Xpress-SLP modeling:

General Expressions

A general expression is an expression which involves decision variables (type mpvar), not necessarily in a linear way. mpvar objects are recognized as such and are treated differently from ordinary real numbers. General expressions are made up of the following:

Any linear expression is also a general expression. However, expressions involving mpvar objects in a nonlinear way can only be used in general expressions. This includes Mosel functions used on items which are not constant, except for the aggregation functions sum and prod. Many of the Mosel mathematical functions can be used directly on non-constant expressions; more complex functions, including those defined by the user, must use the special SLP function form Func.

Examples:
 t + u*v/w + x ^ 0.5 + y/z
 sum (i in 1..10) x(i)*y(i)
 sin(x/y)

where t, u, v, w, x, y, z are variables

gexps can be used in printing statements. For example:

writeln("myGexp = ",myGexp)

where myGexp is a general expression. After a solution has been found, the value of the expression will be printed where possible. Because of limitations in recalculating functions, this facility is generally not available for gexps that contain function references.

General Constraint Expressions

A general constraint is built up from general expressions. The allowable forms are:

GenExp1 Ctr_cmp GenExp2
LinExp1 Ctr_cmp GenExp2
GenExp1 Ctr_cmp LinExp2

where GenExp1 and GenExp2 are general expressions; LinExp1 and LinExp2 are linear expressions involving constants or mpvars; and Ctr_cmp is one of the constraint comparators Maths/geq.png, = and Maths/leq.png.

Examples:

gx <= 42        ! where gx is an object of type gexp
x^2 + y^2 <=4   ! where x and y are objects of type mpvar
x/y = a/b       ! where x, y, a, b are objects of type linctr, gexp or mpvar

A general constraint expression can be assigned to an identified object of type genctr but can also be stated on its own, as an anonymous constraint. Values (for example, dual or slack values, or coefficients) of named genctr objects can be accessed but no values can be obtained for anonymous constraints.

Examples:

gc:= gx <= 42 ! assigns the constraint to the genctr object gc
gx <= 42      ! has the same effect, but the constraint is anonymous

If the left hand side or right hand side of a general constraint is a linear expression, then the modeling of the linear expression will be retained when the matrix is constructed. That is, the coefficients of the variables in the linear expression will appear as constant coefficients in the matrix. Any items which are in a general expression will be aggregated into a single formula.

The following example shows how different implementations of a constraint arise from different ways of formulating it:

declarations
  c: array (1..4) of genctr
  x, y: mpvar
end-declarations

x <= 10
y <= 10

c(1):= x^2 + y^2         <= 4 - x
c(2):= x^2 + y^2 + x     <= 4
c(3):= x^2 + y^2 + x - 4 <= 0
c(4):= x^2 + y^2         <= 4 - x^1

SLPloadprob(x+y)
SLPmaximize

forall (i in 1..4)
  writeln(" c(",i,")=",getact(c(1)),
          " (c(",i,",),x)=",getcoeff(c(1),x),
          " (c(",i,",),)=",getcoeff(c(1)),
          " (c(",i,",),nonlin)=",getnlcoeff(c((i)))

Constraints c(1) to c(4) are mathematically identical. However, because of the different ways in which the expression is split into linear and nonlinear parts, the ways in which they are loaded into the problem are different. With an optimal solution of x=0.962666 and y=1.452960, the printed results are as follows:

 c(1)=4 (c(1),x)=1 (c(1),)=-4 (c(1),nonlin)=3.03782
 c(2)=4 (c(2),x)=0 (c(2),)=-4 (c(1),nonlin)=4.00049
 c(3)=0 (c(3),x)=0 (c(3),)=0 (c(3),nonlin)=0.000486
 c(4)=0 (c(4),x)=0 (c(4),)=0 (c(4),nonlin)=0.000486

The explanation is as follows: all constraints (for example X Maths/leq.png Y) are rewritten with a right hand side of zero (for example X - Y Maths/leq.png 0). It is this latter form which is used when returning values through getsol, getcoeff or getnlcoeff. In more detail:

c(1)  This is loaded as the constraint

c(1):= ( x^2 + y^2 ) + ( x ) <= 4
because the original right hand side is a linear expression which can be separated. It has a constant term of -4 (always interpreted as if it was on the left hand side). The first bracketed term on the left hand side is a (nonlinear) gexp and the second is a linear term arising from the rearrangement of the linear right hand of the original formulation. The coefficient of x is therefore 1 because x appears explicitly in a linear term. The activity of the constraint is 4 (the value of the left hand side of the rearrangement). The value of the nonlinear part is the value of x2 +y2

c(2)  This is loaded as the constraint

c(2):= ( x^2 + y^2 + x ) <= 4
because the original right hand side is a constant, and the left hand side is a gexp. It has a constant term of -4 (always interpreted as if it was on the left hand side). The bracketed term on the left hand side is a (nonlinear) gexp. The coefficient of x is therefore 0 because x appears only in a gexp. The activity of the constraint is 4 (the value of the left hand side of the rearrangement). The value of the nonlinear part is also 4, within the tolerance of the convergence.

c(3)  This is loaded as the constraint

c(3):= ( x^2 + y^2 + x - 4) <= 0
because the original left hand side is a gexp. It has a constant term of 0. The bracketed term on the left hand side is a (nonlinear) gexp. The coefficient of x is therefore 0 because x appears only in a gexp. The activity of the constraint is 0 (the value of the left hand side of the rearrangement). The value of the nonlinear part is also 0, within the tolerance of the convergence.

c(4)  This is loaded as the constraint

c(4):= ( x^2 + y^2 ) + ( x - 4) <= 0
because the original left hand side and right hand side are both gexps (although the right hand side is actually linear, the presence of the exponent for an mpvar forces Mosel to treat it as a nonlinear expression). It has a constant term of 0. The bracketed terms on the left hand side are both gexps, which are amalgamated into one general expression. The coefficient of x is therefore 0 because x appears only in a gexp. The activity of the constraint is 0 (the value of the left hand side of the rearrangement). The value of the nonlinear part is also 0, within the tolerance of the convergence.

genctrs can be used in printing statements. For example:

writeln("myGenctr = ",myGenctr)

where myGenctr is a general constraint. After a solution has been found, the value of the constraint will be printed where possible. Because of limitations in recalculating functions, this facility is generally not available for genctrs that contain function references. A genctr contained in the problem will return its activity, slack and dual values.

xvitem Objects

An xvitem corresponds to a single item in an Xpress-SLP entity of type XV (an extended variable array). The main purpose of the XV is to pass complicated lists of parameters to external functions, but the structure does have wider application. Within Mosel, the fundamental object is the xvitem, although most of the data handling is done with arrays of xvitems. Following the convention of Xpress-SLP, we shall refer to an array of objects of type xvitem as an XV.

Because an XV is a (one-dimensional) array of xvitem objects, an array of XVs is represented as a multi-dimensional array of xvitem objects. In such a case, an individual XV is described by using the reference of the first item in the XV, rather than the name of the XV array itself (see the example below in Extended variable arrays (XVs)).

An xvitem object is initialized using the XVitem function.

Examples:

XX(1) := XVitem(C, "Capital")
XX(2) := XVitem("Rate", I+2)
XX(3) := XVitem("Term",3.5)

XX(1) is defined as the mpvar C which is given the name Capital for the function
XX(2) is defined as the formula I+2 where i is an mpvar which is given the name Rate for the function
XX(3) is defined as the constant 3.5 which is given the name Term for the function

Although it is premature to explain fully about the way user functions are implemented in Mosel, the following function shows how variable names can be used:

function Interest(Values:array(vRange:range) of real,
                  FuncInfo:array(fRange:range) of integer,
                  Names:array(nRange:range) of string): real
 declarations
  n: integer
  Capital, Rate, Term: real
 end-declarations
  Capital := 100000
  Rate := 5
  Term:= 25
  n := FuncInfo(fRange(1))
 forall (i in 1..n) do
  case Names(nRange(i)) of
   "Capital" : Capital := Values(vRange(i))
   "Rate"    : Rate    := Values(vRange(i))
   "Term"    : Term    := Values(vRange(i))
  end-case
 end-do
 returned := Capital*(Rate/100)*Term
end-function

Without going into detail about the function, it is easy to see that it calculates a total amount of simple interest payable on a given amount of capital, at a given rate, for a given amount of time. If any of these pieces of information is missing, then a default value is used. The function is flexible because it does not require the information in any particular order and is robust because it will always give an answer. If additional information is given, it simply ignores it.

Extended variable arrays (XVs)

An XV is an array of xvitem objects. As well as allowing features such as named arguments for functions, an XV provides a convenient way of passing the same information to more than one function. For example, there might be a number of different ways of borrowing money, with different repayment conditions. The same basic information (amount, interest rate and term) is always required, but the calculations are different (for example, simple interest, compound interest, partial repayments of capital). In a model, the same data can be passed to several functions, for example:

declarations
 XX:array(1..n) of xvitem
 C,I:mpvar ! capital and interest rate
 Cost:mpvar 
end-declarations
 ...
 Cost is_free
 Cost >= -Interest1(XX)
 Cost >= -Interest2(XX)
 Cost >= -Interest3(XX)
 ...
 SLPloadprob(Cost)
 SLPminimize

Defining the members of XX as in the previous section, functions Interest1 etc calculate the interest payable on different types of loan. The objective Cost is minimized and is therefore the greatest of all the "- interest" values - that is, -Cost is the smallest interest payable.

An XV is a 1-dimensional array of xvitems. An array of Xvs must therefore be represented by a multi-dimensional array of xvitems — for example, a 2-dimensional array of XVs is represented by a 3-dimension array of xvitems. Extending the earlier example to (say) three different loans, we would have:

declarations
 XVA: array(1..3,1..n) of xvitem
 C, I:array(1..3) of mpvar   ! capital borrowed and interest rates
end-declarations
forall (i in 1..3) do
 XVA(i,1) := XVitem(C(i), "Capital")
 XVA(i,2) := XVitem("Rate", I(i)+2)
 XVA(i,3) := XVitem("Term",3.5)
 XVA(i,4) := XVitem("Repayment",50)
end-do

When referring to one of the XVs in an array, use the first item of the XV. For example, to refer to XV number 2 in the array XVA use XVA(2,1). The cost constraints might then be similar to the following:

Cost >= -sum(i in 1..3) Interest1(XVA(i,1))

or

Cost(i) >= -(Interest1(XVA(i,1))+
             Interest2(XVA(i,1))+
             Interest3(XVA(i,1)))

Arrays and XVs

A real array can be used in place of an XV (array of xvitems) whenever the XV would contain only constant values (no names, formulae or variables). Normally, only 1-dimensional arrays are supported so, if there is a need for multi-dimensional references, they must still be done as XVs.

Example:

declarations
 XVA: array(1..n) of xvitem
 RA: array(1..n) of real
 X:mpvar
end-declarations
SLPDATA("XV",XVA)
forall(i in 1..n) XV(i) := XVitem(RA(i))
C1:= Func("myFunc",X,XVA) <= 1

In the example, XVA is declared as an XV and initialized with values from the real array RA. It is then used in the constraint C1 inside a function which takes one variable (X) as well as the XV of constants. Because XVA contains only constants, the same constraint can be written using the array RA instead.

declarations
 RA: array(1..n) of real
 X:mpvar
end-declarations
C1:= Func("myFunc",X,RA) <= 1

Because there is no provision for the use of real arrays as multi-dimensional XVs, the following example cannot be rewritten to avoid the use of XVs:

declarations
 XVA: array(1..n,2) of xvitem
 RA: array(1..n,2) of real
 X:mpvar
end-declarations
SLPDATA("XV",XVA)
forall(i in 1..n, j in 1..2) XV(i,j) := XVitem(RA(i,j))
C1:= Func("myFunc",X,XVA(1,2)) <= 1

Normally, only 1-dimensional real arrays are used as substitutes for XVs. However, it is recognized that there are occasions when it is easier to maintain data in a multi-dimensional array, and use it directly as an XV. If this feature is used, the data will be converted into a 1-dimensional XV by storing the individual elements of the array in order with the right-hand index changing fastest. So, with a 2-dimensional array A(1..3,1..2), the values are stored in the order
A(1,1), A(1,2), A(2,1), A(2,2), A(3,1), A(3,2).

To prevent the system rejecting the use of a multi-dimensional array in this way, the parameter xslp_maxxvarraydimension must be set to a value greater than or equal to the dimension of the array (in this case, 2).

Example:

declarations
 RA: array(1..n,1..2) of real
 X:mpvar
end-declarations
setparam("xslp_MaxXVArrayDimension",2)
C1:= interp(X,RA) <= 15


If you have any comments or suggestions about these pages, please send mail to docs@dashoptimization.com.