Initializing help system before first use

Drawing user graphs

In this chapter we show how to draw a user-defined SVG graph. The graph we wish to display is generated as a result of repeated executions of a model with different parameter settings. So we shall first see an example of writing a simple algorithm in the Mosel language involving

  • re-definition of constraints,
  • repeated re-optimization,
  • saving solution information,
  • definition of a user graph: drawing points, lines, and texts, and
  • simple programming tasks (loops and selections).

Extended problem description

In addition to the data considered so far (see table in Chapter 2), the investor now also has at hand the estimations of the deviations from the expected return per share (Table Estimated deviations). With this additional information, he decides to run the LP model with different limits on the portion of high-risk shares and to represent the results as a graph, plotting the resulting total return against the deviation as a measure of risk.

Table 6.1: Estimated deviations
Number Description Deviation
1 treasury 0.1
2 hardware 19
3 theater 28
4 telecom 22
5 brewery 4
6 highways 3.5
7 cars 5
8 bank 0.5
9 software 25
10 electronics 16

Looping over optimization

We are going to modify the model foliodata.mos from the previous chapter in such a way that the problem is re-optimized repeatedly with different limits on the percentage of high-risk values.

In detail, the model will be transformed to implement the following algorithm:

  1. Definition of the part of the model that remains unchanged by the parameter changes.
  2. For every parameter value:
    • Re-define the constraint limiting the percentage of high-risk values.
    • Solve the resulting problem.
    • If the problem is feasible: store the solution values.
  3. Draw the result graph.

To store the solution value and the total estimated deviation of the result after each optimization run, we declare the following two arrays:

 declarations
  SOLRET: array(range) of real       ! Solution values (total return)
  SOLDEV: array(range) of real       ! Solution values (average deviation)
 end-declarations

The following code fragment introduces a loop around the definition of the constraint limiting the portion of high-risk shares and the solution procedure. To be able to override its previous definition at every iteration, we now give this constraint a name, Risk. If the constraint did not have a name, then each time the loop was executed, a new constraint would be added, and the existing constraint would not be replaced.

 ct:=0
 forall(r in 0..20) do
  ! Limit the percentage of high-risk values
   Risk:= sum(s in RISK) frac(s) <= r/20

   maximize(Return)                  ! Solve the problem
   if (getprobstat = XPRS_OPT) then  ! Save the optimal solution value
    ct+=1
    SOLRET(ct):= getobjval
    SOLDEV(ct):= getsol(sum(s in SHARES) DEV(s)*frac(s))
   else
    writeln("No solution for high-risk values <= ", 100*r/20, "%")
   end-if
 end-do

Above we have used the second form of the forall loop, namely forall/do. This form must be used when several statements are included in the loop. The loop is terminated by end-do.

Another new feature in this code extract is the if/then/else/end-if statement. We only want to save the values for a problem instance if the optimal solution has been found—the solution status is obtained with function getprobstat and tested whether it is `solved to optimality', represented by the constant XPRS_OPT.

The selection statement has two other forms, if/then/end-if and if/then/elif/then/ else/end-if where elif/then may be repeated several times.

For further examples and a complete description of all loops and selection statements available in Mosel the reader is refered to the `Mosel User Guide'.

Drawing a user graph

We now have gathered all the data required to draw the graph. Graphing functions are provided by the module mmsvg (documented in the `Mosel Language Reference Manual'), so it needs to be loaded at the beginning of the model by adding the following line:

 uses "mmsvg"

Then the following lines draw the graph (note the use of the sum operator to create a list of points):

 svgaddgroup("GrS", "Solution values", SVG_GREY)
 forall(r in 1..ct) svgaddpoint("GrS", SOLRET(r), SOLDEV(r))
 svgaddline("GrS", sum(r in 1..ct) [SOLRET(r), SOLDEV(r)])

The user graph will be displayed in the editor window of the Workbench workspace. Select the tab SVG drawing to move it to the foreground. With the above we obtain the following output (due to the interplay of the various constraints the resulting graph is not a straight line as one might have expected at first thought):

Chap45/graphone.png

Figure 6.1: Plot of the result graph

In addition to this graph, we may also display labeled points representing the input data (`GrL' for low risk shares and `GrH' for high risk shares):

 svgaddgroup("GrL", "Low risk", SVG_GREEN)
 svgaddgroup("GrH", "High risk", SVG_RED)
	
 forall(s in SHARES - RISK) do
  svgaddpoint("GrL", RET(s), DEV(s))
  svgaddtext("GrL", RET(s)+1, 1.3*(DEV(s)-1), s)
 end-do

 forall(s in RISK) do
  svgaddpoint("GrH", RET(s), DEV(s))
  svgaddtext("GrH", RET(s)-2.5, DEV(s)-1, s)
 end-do

Notice the set notation: SHARES - RISK means `all elements of SHARES that are not contained in RISK'.

The complete output now is:

Chap45/graphthree.png

Figure 6.2: Plot of result graph and data

Complete example

The complete model file folioloop_graph.mos with all the features discussed in this chapter looks as follows. Notice that the two modules mmxprs and mmive may be loaded with a single uses statement. The deviation data may either be added to the original data file or, as shown here, read from a second file.

model "Portfolio optimization with LP"
 uses "mmxprs", "mmsvg"             ! Use Xpress Optimizer with SVG graphing

 parameters
  DATAFILE= "folio.dat"             ! File with problem data
  DEVDATA= "foliodev.dat"           ! File with deviation data
  MAXVAL = 0.3                      ! Max. investment per share
  MINAM = 0.5                       ! Min. investment into N.-American values
 end-parameters

 declarations
  SHARES: set of string             ! Set of shares
  RISK: set of string               ! Set of high-risk values among shares
  NA: set of string                 ! Set of shares issued in N.-America
  RET: array(SHARES) of real        ! Estimated return in investment
  DEV: array(SHARES) of real        ! Standard deviation
  SOLRET: array(range) of real      ! Solution values (total return)
  SOLDEV: array(range) of real      ! Solution values (average deviation)
 end-declarations

 initializations from DATAFILE
  RISK RET NA
 end-initializations

 initializations from DEVDATA
  DEV
 end-initializations

 declarations
  frac: array(SHARES) of mpvar      ! Fraction of capital used per share
  Return, Risk: linctr              ! Constraint declaration (optional)
 end-declarations

! Objective: total return
 Return:= sum(s in SHARES) RET(s)*frac(s)

! Minimum amount of North-American values
 sum(s in NA) frac(s) >= MINAM

! Spend all the capital
 sum(s in SHARES) frac(s) = 1

! Upper bounds on the investment per share
 forall(s in SHARES) frac(s) <= MAXVAL

! Solve the problem for different limits on high-risk shares
 ct:=0
 forall(r in 0..20) do
  ! Limit the percentage of high-risk values
   Risk:= sum(s in RISK) frac(s) <= r/20

   maximize(Return)                  ! Solve the problem

   if (getprobstat = XPRS_OPT) then  ! Save the optimal solution value
    ct+=1
    SOLRET(ct):= getobjval
    SOLDEV(ct):= getsol(sum(s in SHARES) DEV(s)*frac(s))
   else
    writeln("No solution for high-risk values <= ", 100*r/20, "%")
   end-if
 end-do

! Drawing a graph to represent results (`GrS') and data (`GrL' & `GrH')
 svgaddgroup("GrS", "Solution values", SVG_GREY)
 svgaddgroup("GrL", "Low risk", SVG_GREEN)
 svgaddgroup("GrH", "High risk", SVG_RED)

 forall(r in 1..ct) svgaddpoint("GrS", SOLRET(r), SOLDEV(r))
 svgaddline("GrS", sum(r in 1..ct) [SOLRET(r), SOLDEV(r)])

 forall(s in SHARES - RISK) do
  svgaddpoint("GrL", RET(s), DEV(s))
  svgaddtext("GrL", RET(s)+1, 1.3*(DEV(s)-1), s)
 end-do

 forall(s in RISK) do
  svgaddpoint("GrH", RET(s), DEV(s))
  svgaddtext("GrH", RET(s)-2.5, DEV(s)-1, s)
 end-do

! Scale the size of the displayed graph
 svgsetgraphscale(10)
 svgsetgraphpointsize(2)

! Optionally save graphic to file
 svgsave("foliograph.svg")

! Display the graph and wait for window to be closed by the user
 svgrefresh
 svgwaitclose

end-model

The problem is not feasible for small limit values on the constraint Risk. Besides the graphs we therefore obtain the following text output:

No solution for high-risk values <= 0%
No solution for high-risk values <= 5%
No solution for high-risk values <= 10%
No solution for high-risk values <= 15%