FICO Xpress OptimizationR

Last update 8 April, 2020

5.2
USER GUIDEFICO Xpress MoselR©2001–2020 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac
Corporation ("FICO"). Receipt or possession of this documentation does not convey rights to disclose,
reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation
purposes to determine whether to purchase a license to the software described in this documentation, oras otherwise set forth in a written software license agreement between you and FICO (or a FICO afﬁliate).
Use of this documentation and the software described in it must conform strictly to the foregoing
permitted uses, and no other use is permitted.
The information in this documentation is subject to change without notice. If you ﬁnd any problems in thisdocumentation, please report them to us in writing. Neither FICO nor its afﬁliates warrant that thisdocumentation is error-free, nor are there any other warranties with respect to the documentation except
as may be provided in the license agreement. FICO and its afﬁliates speciﬁcally disclaim any warranties,
express or implied, including, but not limited to, non-infringement, merchantability and ﬁtness for aparticular purpose. Portions of this documentation and the software described in it may contain copyrightof various authors and may be licensed under certain third-party licenses identiﬁed in the software,
documentation, or both.
In no event shall FICO or its afﬁliates be liable to any person for direct, indirect, special, incidental, or
consequential damages, including lost proﬁts, arising out of the use of this documentation or the software
described in it, even if FICO or its afﬁliates have been advised of the possibility of such damage. FICO and
its afﬁliates have no obligation to provide maintenance, support, updates, enhancements, or modiﬁcations
except as required to licensed users under a license agreement.
FICO is a registered trademark of Fair Isaac Corporation in the United States and may be a registeredtrademark of Fair Isaac Corporation in other countries. Other product and company names herein may be
trademarks of their respective owners.
FICO R Xpress Mosel
Deliverable Version: A
Last Revised: 8 April, 2020
Version 5.2

Contents
I

Using the Mosel language

1

Introduction
Why you need Mosel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
What you need to know before using Mosel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Symbols and conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The structure of this guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2
2
2
3
4

1

Getting started with Mosel
1.1 Entering a model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 The chess set problem: description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.1 A ﬁrst formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Solving the chess set problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.1 Building the model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.2 Obtaining a solution using Mosel . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.3 Running Mosel from a command line . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.4 Using Xpress Workbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5
5
5
5
6
6
7
8
9

2 Some illustrative examples
2.1 The burglar problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.1 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.3 The burglar problem revisited . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 A blending example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.1 The model background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.2 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.4 Re-running the model with new data . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.5 Reading data from spreadsheets and databases . . . . . . . . . . . . . . . . . . .
2.2.5.1 Excel spreadsheets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.5.2 Database example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.5.3 Generic spreadsheet example . . . . . . . . . . . . . . . . . . . . . . . . .

11
11
11
11
14
16
16
16
16
18
18
19
20
21

3 More advanced modeling features
3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 A transport example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.1 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Conditional generation — the | operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1 Conditional variable creation and create . . . . . . . . . . . . . . . . . . . . . . .
3.4 Reading sparse data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4.1 Data input with initializations from . . . . . . . . . . . . . . . . . . . . . . .
3.4.2 Data input with readln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4.3 Data input with diskdata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5 I/O error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23
23
23
23
24
26
26
28
28
28
29
30

Fair Isaac Corporation Conﬁdential and Proprietary Informationi

Contents

4 Integer Programming
4.1 Integer Programming entities in Mosel . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 A project planning model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2.1 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3 The project planning model using Special Ordered Sets . . . . . . . . . . . . . . . . . . . .

32
32
34
34
35
36

5 Overview of subroutines and reserved words
5.1 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Reserved words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38
39
40

6 Correcting errors in Mosel models
6.1 Correcting syntax errors in Mosel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Correcting run time errors in Mosel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41
41
42

II

44

Advanced language features

Overview

45

7 Flow control constructs
7.1 Selections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.1 forall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.1.1 Multiple indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.1.2 Conditional looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.1.3 Counters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.2 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.3 repeat until . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46
46
48
48
49
49
49
50
51

8 Arrays, sets, lists, and records
8.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.1.1 Array declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.1.1.1 Multiple indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.1.1.2 create . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.1.2 Array initialization from ﬁle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.1.3 Automatic arrays: the array operator . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2 Initializing sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2.1 Constant sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2.2 Set initialization from ﬁle, ﬁnalized and ﬁxed sets . . . . . . . . . . . . . . . . . . .
8.3 Working with sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.3.1 Set operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.4 Initializing lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.4.1 Constant list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.4.2 List initialization from ﬁle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.5 Working with lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.5.1 Enumeration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.5.2 List operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.5.3 List handling functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.6 Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.6.1 Deﬁning records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.6.2 Initialization of records from ﬁle . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.7 User types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53
53
54
54
55
55
56
57
57
57
59
60
61
61
61
62
62
62
63
65
65
66
68

9 Functions and procedures
9.1 Subroutine deﬁnition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70
70

Fair Isaac Corporation Conﬁdential and Proprietary Informationii

Contents

9.2
9.3
9.4
9.5

Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
forward . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Overloading of subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71
72
73
74

10 Output
10.1 Producing formatted output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2 File output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2.1 Data output with initializations to . . . . . . . . . . . . . . . . . . . . . . .
10.2.2 Data output with writeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2.3 Data output with diskdata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2.4 Solution output with initializations to . . . . . . . . . . . . . . . . . . . . .
10.3 Real number format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76
76
78
78
79
79
80
81

11 More about Integer Programming
11.1 Cut generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1.1 Example problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1.2 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1.4 Cut-and-Branch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1.5 Comparison tolerance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1.6 Branch-and-Cut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2 Column generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2.1 Example problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2.2 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2.4 Alternative implementation: Working with multiple problems . . . . . . . . . . . . .

83
83
83
83
84
86
87
87
89
89
89
90
93

12 Extensions to Linear Programming
12.1 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.1.1 Example problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.1.2 Model formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.1.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.2 Goal Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.2.1 Example problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.2.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95
95
95
95
96
98
98
98

III

Working with the Mosel libraries

Overview

101
102

13 C interface
103
13.1 Basic tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
13.1.1 Compiling a model in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
13.1.2 Executing a model in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
13.1.3 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
13.2 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
13.3 Accessing modeling objects and solution values . . . . . . . . . . . . . . . . . . . . . . . . 105
13.3.1 Accessing sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
13.3.2 Retrieving solution values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
13.3.3 Sparse arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
13.4 Exchanging data between an application and a model . . . . . . . . . . . . . . . . . . . . . 108
13.4.1 Dense arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
13.4.2 Sparse arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
13.4.3 Dynamic data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Fair Isaac Corporation Conﬁdential and Proprietary Informationiii

Contents

13.4.4 Scalars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
13.5 Redirecting the Mosel output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
13.6 Problem solving in C with Xpress Optimizer . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
14 Other programming language interfaces
120
14.1 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
14.1.1 Compiling and executing a model in Java . . . . . . . . . . . . . . . . . . . . . . . . 120
14.1.2 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
14.1.3 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
14.1.4 Accessing sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
14.1.5 Retrieving solution values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
14.1.6 Sparse arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
14.1.7 Exchanging data between an application and a model . . . . . . . . . . . . . . . . 124
14.1.7.1 Dense arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
14.1.7.2 Sparse arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
14.1.7.3 Dynamic data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
14.1.7.4 Scalars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
14.1.8 Redirecting the Mosel output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
14.2 .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
14.2.1 Compiling and executing a model in C# . . . . . . . . . . . . . . . . . . . . . . . . . 131
14.2.2 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
14.2.3 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
14.2.4 Accessing sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
14.2.5 Retrieving solution values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
14.2.6 Sparse arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
14.2.7 Exchanging data between an application and a model . . . . . . . . . . . . . . . . 135
14.2.7.1 Dense arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
14.2.7.2 Sparse arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
14.2.7.3 Dynamic data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
14.2.7.4 Scalars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
14.2.8 Redirecting the Mosel output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
14.3 VBA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
14.3.1 Compiling and executing a model in VBA . . . . . . . . . . . . . . . . . . . . . . . . 143
14.3.2 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
14.3.3 Redirecting the Mosel output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

IV

Extensions and tools

Overview

147
148

15 Debugger and Proﬁler
149
15.1 The Mosel Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
15.1.1 Using the Mosel Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
15.1.1.1 Debugging concurrent models . . . . . . . . . . . . . . . . . . . . . . . . 151
15.1.2 Debugger in Xpress Workbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
15.2 Efﬁcient modeling through the Mosel Proﬁler . . . . . . . . . . . . . . . . . . . . . . . . . . 152
15.2.1 Using the Mosel Proﬁler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
15.2.1.1 Proﬁling concurrent models . . . . . . . . . . . . . . . . . . . . . . . . . . 154
15.2.2 Other commands for model analysis . . . . . . . . . . . . . . . . . . . . . . . . . . 154
15.2.3 Some recommendations for efﬁcient modeling . . . . . . . . . . . . . . . . . . . . 155
16 Packages
157
16.1 Deﬁnition of constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
16.2 Deﬁnition of subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
16.3 Deﬁnition of types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

Fair Isaac Corporation Conﬁdential and Proprietary Informationiv

Contents

16.4 Deﬁnition of parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
16.5 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
16.6 Packages vs. modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
17 Language extensions
167
17.1 Generalized ﬁle handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
17.1.1 Displaying the available I/O drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
17.1.2 List of I/O drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
17.2 Multiple models and parallel solving with mmjobs . . . . . . . . . . . . . . . . . . . . . . . 174
17.2.1 Running a model from another model . . . . . . . . . . . . . . . . . . . . . . . . . . 174
17.2.2 Compiling to memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
17.2.3 Exchanging data between models . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
17.2.4 Distributed computing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
17.3 Graphics and GUIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
17.3.1 Drawing user graphs with mmsvg . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
17.3.2 XML and HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
17.3.2.1 mmxml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
17.3.2.2 Reading and writing XML data . . . . . . . . . . . . . . . . . . . . . . . . . 180
17.3.2.3 Generating HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
17.3.3 Xpress Insight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
17.4 Solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
17.4.1 QCQP solving with Xpress Optimizer . . . . . . . . . . . . . . . . . . . . . . . . . . 187
17.4.2 Xpress NonLinear . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
17.4.3 Xpress Kalis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
17.5 Date and time data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
17.5.1 Initializing dates and times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
17.5.2 Dates and times as constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
17.5.3 Conversion to and from numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
17.5.4 Operations and access functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
17.6 Text handling and regular expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
17.6.1 text vs. string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
17.6.2 Parsing text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
17.6.3 Regular expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
18 Annotations
200
18.1 Accessing annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
18.2 moseldoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

V

Remote invocation of Mosel

Overview

207
208

19 XPRD C
209
19.1 Exchanging data with the model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
20 XPRD Java214
20.1 Exchanging data with the model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

Appendix

219

A Mosel Language overview
220
A.1 Structure of a Mosel model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
A.2 Data structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
A.3 Selection statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

Fair Isaac Corporation Conﬁdential and Proprietary Informationv

Contents

A.4
A.5
A.6
A.7
A.8

Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Built in functions and procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Constraint handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Problem handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

222
223
224
226
226

B Good modeling practice with Mosel
228
B.1 Using constants and parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
B.2 Naming sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
B.3 Finalizing sets and dynamic arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
B.4 Ordering indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
B.5 Use of exists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
B.6 Structuring a model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
B.7 Transforming subroutines into user modules . . . . . . . . . . . . . . . . . . . . . . . . . . 232
B.8 Algorithm choice and parameter settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
C Character encoding in Mosel
234
C.1 What is a "character encoding", "character map", "code page"? . . . . . . . . . . . . . . . . 234
C.2 What is Unicode? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
C.3 What is the meaning of UTF-8,16,32 and UCS-2? . . . . . . . . . . . . . . . . . . . . . . . . 235
C.4 What is a BOM? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
C.5 Which character encoding is conﬁgured on my computer? . . . . . . . . . . . . . . . . . . 235
C.6 Which ﬁles are concerned by character encoding in Mosel? . . . . . . . . . . . . . . . . . 236
C.7 How can I convert the character encoding of a text ﬁle? . . . . . . . . . . . . . . . . . . . . 236
D Contacting FICO
238
Product support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
Product education . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
Product documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
Sales and maintenance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Related services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
FICO Community . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
About FICO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239

Index

Fair Isaac Corporation Conﬁdential and Proprietary Information

240

vi

I. Using the Mosel language

Introduction
Why you need Mosel‘Mosel’ is not an acronym. It is pronounced like the German river, mo-zul. It is an advanced modelingand solving language and environment, where optimization problems can be speciﬁed and solved withthe utmost precision and clarity.
Here are some of the features of Mosel









Mosel’s easy syntax is regular and described formally in the reference manual.
Mosel supports dynamic objects, which do not require pre-sizing. For instance, you do not have to
specify the maximum sizes of the indices of a variable x.
Mosel models are pre-compiled. Mosel compiles a model into a binary ﬁle which can be run on
any computer platform, and which hides the intellectual property in the model if so required.
Mosel is embeddable. There is a runtime library which can be called from your favorite
programming language if required. You can access any of the model’s objects from yourprogramming language.
Mosel is easily extended through the concept of modules. It is possible to write a set of functions,which together stand alone as a module. Several modules are supplied with the Mosel
distribution, including Xpress Optimizer.

Support for user-written functions and procedures is provided.



The use of sets of objects is supported.

Constraints and variables etc. can be added incrementally. For instance, column generation candepend on the results of previous optimizations, so subproblems are supported.

The modeling component of Mosel provides you with an easy to use yet powerful language fordescribing your problem. It enables you to gather the problem data from text ﬁles and a range of
popular spreadsheets and databases, and gives you access to a variety of solvers, which can ﬁnd
optimal or near-optimal solutions to your model.

What you need to know before using Mosel
Before using Mosel you should be comfortable with the use of symbols such as x or y to represent
unknown quantities, and the use of this sort of variable in simple linear equations and inequalities, forexample:x+y ≤6
Experience of a basic course in Mathematical or Linear Programming is worthwhile, but is notessential. Similarly some familiarity with the use of computers would be helpful.

Fair Isaac Corporation Conﬁdential and Proprietary Information2

Introduction

For all but the simplest models you should also be familiar with the idea of summing over a range ofvariables. For example, if producej is used to represent the number of cars produced on production line
j then the total number of cars produced on all N production lines can be written as:NX

producej

j=1

This says ‘sum the output from each production line producej over all production lines j from j = 1 to
j = N’.
If our target is to produce at least 1000 cars in total then we would write the inequality:
NX

producej ≥ 1000

j=1

We often also use a set notation for the sums. Assuming that LINES is the set of production lines{1, .., N}, we may write equivalently:X
producej ≥ 1000j∈LINESThis may be read ‘sum the output from each production line producej over all production lines j in theset LINES’.
Other common mathematical symbols that are used in the text are IN (the set of non-negative integer
numbers {0, 1, 2, ...}), ∩ and ∪ (intersection and union of sets), ∧ and ∨ (logical ‘and’ and ‘or’), theall-quantiﬁer ∀ (read ‘for all’), and ∃ (read ‘exists’).
Mosel closely mimics the mathematical notation an analyst uses to describe a problem. So provided
you are happy using the above mathematical notation the step to using a modeling language will bestraightforward.

Symbols and conventions
We have used the following conventions within this guide:











Mathematical objects are presented in italics.
Examples of commands, models and their output are printed in a Courier font. Filenames are
given in lower case Courier.
Decision variables have lower case names; in most example problems these are verbs (such asuse, take).
Constraint names start with an upper case letter, followed by mostly lower case (e.g. Proﬁt,
TotalCost).
Data (arrays, sets, lists) and constants are written entirely with upper case (e.g. DEMAND, COST,ITEMS).
The vertical bar symbol | is found on many keyboards as a vertical line with a small gap in the
middle, but often confusingly displays on-screen without the small gap. In the UNIX world it isreferred to as the pipe symbol. (Note that this symbol is not the same as the character sometimes
used to draw boxes on a PC screen.) In ASCII, the | symbol is 7C in hexadecimal, 124 in decimal.

Fair Isaac Corporation Conﬁdential and Proprietary Information

3

Introduction

The structure of this guideThis user guide is structured into these main parts








Part I describes the use of Mosel for people who want to build and solve Mathematical
Programming (MP) problems. These will typically be Linear Programming (LP), Mixed Integer
Programming (MIP), or Quadratic Programming (QP) problems. The part has been designed toshow the modeling aspects of Mosel, omitting most of the more advanced programming
constructs.
Part II is designed to help those users who want to use the powerful programming language
facilities of Mosel, using Mosel as a modeling, solving and programming environment. Items
covered include looping (with examples), more about using sets, producing nicely formatted
output, functions and procedures. We also give some advanced MP examples, including
Branch-and-Cut, column generation, Goal Programming and Successive Linear Programming.
Part III shows how Mosel models can be embedded into large applications using programming
languages like C, Java, or C#.
Part IV gives examples of some of the advanced features of Mosel, including the use of the Mosel
Debugger and Proﬁler for the development and analysis of large-scale Mosel models, anintroduction to the notion of packages, and an overview of the functionality of the modules in the
Mosel distribution.

This user guide is deliberately informal and is not complete. It must be read in conjunction with theMosel reference manual, where features are described precisely and completely.

Fair Isaac Corporation Conﬁdential and Proprietary Information

4

CHAPTER 1

Getting started with Mosel
1.1

Entering a model
In this chapter we will take you through a very small manufacturing example to illustrate the basic
building blocks of Mosel.
Models are entered into a Mosel ﬁle using a standard text editor (do not use a word processor as an
editor as this may not produce an ASCII ﬁle).
If you have access to Windows, Xpress Workbench is the model development environment to use. The
Mosel ﬁle is then loaded into Mosel, and compiled. Finally, the compiled ﬁle can be run. This chapter
will show the stages in action.

1.2

The chess set problem: description
To illustrate the model development and solving process we shall take a very small example.
A joinery makes two different sizes of boxwood chess sets. The smaller size requires 3 hours ofmachining on a lathe and the larger only requires 2 hours, because it is less intricate. There are four
lathes with skilled operators who each work a 40 hour week. The smaller chess set requires 1 kg of
boxwood and the larger set requires 3 kg. However boxwood is scarce and only 200 kg per week can beobtained.
When sold, each of the large chess sets yields a proﬁt of $20, and one of the small chess set has a
proﬁt of $5. The problem is to decide how many sets of each kind should be made each week tomaximize proﬁt.

1.2.1

A ﬁrst formulation
Within limits, the joinery can vary the number of large and small chess sets produced: there are thus
two decision variables (or simply variables) in our model, one decision variable per product. We shallgive these variables abbreviated names:
small: the number of small chess sets to makelarge: the number of large chess sets to makeThe number of large and small chess sets we should produce to achieve the maximum contribution to
proﬁt is determined by the optimization process. In other words, we look to the optimizer to tell us thebest values of small, and large.
The values which small and large can take will always be constrained by some physical or technologicallimits: they may be constrained to be equal to, less than or greater than some constant. In our case wenote that the joinery has a maximum of 160 hours of machine time available per week. Three hours are

Fair Isaac Corporation Conﬁdential and Proprietary Information

5

Getting started with Mosel

needed to produce each small chess set and two hours are needed to produce each large set. So thenumber of hours of machine time actually used each week is 3 · small + 2 · large. One constraint is thus:
3 · small + 2 · large ≤ 160 (lathe-hours)
which restricts the allowable combinations of small and large chess sets to those that do not exceed
the lathe-hours available.
In addition, only 200 kg of boxwood is available each week. Since small sets use 1 kg for every set
made, against 3 kg needed to make a large set, a second constraint is:
1 · small + 3 · large ≤ 200 (kg of boxwood)
where the left hand side of the inequality is the amount of boxwood we are planning to use and the righthand side is the amount available.
The joinery cannot produce a negative number of chess sets, so two further non-negativity constraints
are:small ≥ 0
large ≥ 0
In a similar way, we can write down an expression for the total proﬁt. Recall that for each of the large
chess sets we make and sell we get a proﬁt of $20, and one of the small chess set gives us a proﬁt of$5. The total proﬁt is the sum of the individual proﬁts from making and selling the small small sets andthe large large sets, i.e.
Proﬁt = 5 · small + 20 · large
Proﬁt is the objective function, a linear function which is to be optimized, that is, maximized. In this case
it involves all of the decision variables but sometimes it involves just a subset of the decision variables.
In maximization problems the objective function usually represents proﬁt, turnover, output, sales,
market share, employment levels or other ‘good things’. In minimization problems the objectivefunction describes things like total costs, disruption to services due to breakdowns, or other lessdesirable process outcomes.
The collection of variables, constraints and objective function that we have deﬁned are our model. It
has the form of a Linear Programming problem: all constraints are linear equations or inequalities, theobjective function also is a linear expression, and the variables may take any non-negative real value.

1.3

Solving the chess set problem

1.3.1

Building the model
The Chess Set problem can be solved easily using Mosel. The ﬁrst stage is to get the model we havejust developed into the syntax of the Mosel language. Remember that we use the notation that items initalics (for example, small) are the mathematical variables. The corresponding Mosel variables will be
the same name in non-italic courier (for example, small).
We illustrate this simple example by using the command line version of Mosel. The model can beentered into a ﬁle named, perhaps, chess.mos as follows:model "Chess"declarations
small: mpvar
large: mpvar
end-declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information! Number of small chess sets to make! Number of large chess sets to make6

Getting started with Mosel

Profit:= 5⁎small + 20⁎large
Lathe:=
3⁎small + 2⁎large <= 160
Boxwood:=small + 3⁎large <= 200
end-model! Objective function! Lathe-hours! kg of boxwood

Indentations are purely for clarity. The symbol ! signiﬁes the start of a comment, which continues tothe end of the line. Comments over multiple lines start with (! and terminate with !).
Notice that the character ‘⁎’ is used to denote multiplication of the decision variables by the units ofmachine time and wood that one unit of each uses in the Lathe and Boxwood constraints.
The modeling language distinguishes between upper and lower case, so Small would be recognized
as different from small.
Let’s see what this all means.
A model is enclosed in a model / end-model block.
The decision variables are declared as such in the declarations / end-declarations block. Everydecision variable must be declared. LP, MIP and QP variables are of type mpvar. Several decisionvariables can be declared on the same line, so
declarations
small, large: mpvar
end-declarations

is exactly equivalent to what we ﬁrst did. By default, Mosel assumes that all mpvar variables areconstrained to be non-negative unless it is informed otherwise, so there is no need to specify
non-negativity constraints on variables.
Here is an example of a constraint:
Lathe:=

3⁎small + 2⁎large <= 160

The name of the constraint is Lathe. The actual constraint then follows. If the ‘constraint’ isunconstrained (for example, it might be an objective function), then there is no <=, >= or = part.
In Mosel you enter the entire model before starting to compile and run it. Any errors will be signaled
when you try to compile the model, or later when you run it (see Chapter 6 on correcting syntax errors).

1.3.2

Obtaining a solution using Mosel
So far, we have just speciﬁed a model to Mosel. Next we shall try to solve it. The ﬁrst thing to do is tospecify to Mosel that it is to use Xpress Optimizer to solve the problem. Then, assuming we can solve
the problem, we want to print out the optimum values of the decision variables, small and large, andthe value of the objective function. The model becomesmodel "Chess (completed)"
uses "mmxprs"! We shall use Xpress Optimizer</p>declarations
small,large: mpvar
end-declarations! Decision variables: produced quantities

Profit:= 5⁎small + 20⁎large
Lathe:=
3⁎small + 2⁎large <= 160
Boxwood:=small + 3⁎large <= 200! Objective function! Lathe-hours! kg of boxwood

maximize(Profit)! Solve the problem

writeln("Make ", getsol(small), " small sets")
writeln("Make ", getsol(large), " large sets")
writeln("Best profit is ", getobjval)

Fair Isaac Corporation Conﬁdential and Proprietary Information

7

Getting started with Mosel
end-model

The line
uses "mmxprs"

tells Mosel that Xpress Optimizer will be used to solve the LP. The Mosel modules mmxprs module
provides us with such things as maximization, handling bases etc.
The linemaximize(Profit)

tells Mosel to maximize the objective function called Profit.
More complicated are the writeln statements, though it is actually quite easy to see what they do. Ifsome text is in quotation marks, then it is written literally. getsol and getobjval are special Mosel
functions that return respectively the optimal value of the argument, and the optimal objective function
value. writeln writes a line terminator after writing all its arguments (to continue writing on the sameline, use write instead). writeln can take many arguments. The statement
writeln("small: ", getsol(small), " large: ", getsol(large) )will result in the values being printed all on one line.

1.3.3

Running Mosel from a command line
When you have entered the complete model into a ﬁle (let us call it chess.mos), we can proceed to get
the solution to our problem. We start Mosel at the command prompt by typing the following command
mosel execute chess.mosand we will see output something like that below.
Make 0 small sets
Make 66.6667 large sets
Best profit is 1333.33

The Mosel command for executing the model can be abbreviated tomosel exec chessor simply
mosel chess

The model execution performed by the command execute comprises three stages:
1. Compiling chess.mos
2. Loading the compiled model
3. Running the model we have just loaded.
Instead of using execute, we can choose to explicitly generate the compiled model ﬁle chess.bim
mosel compile chess.mos

Fair Isaac Corporation Conﬁdential and Proprietary Information

8

Getting started with Mosel

followed bymosel run chess.bim

to load and run the compiled model.

1.3.4

Using Xpress Workbench
Under Microsoft Windows you may also use Xpress Workbench, a development studio typeenvironment for working with your Mosel models. Xpress Workbench is a complete modeling andoptimization development environment that presents Mosel in an easy-to-use Graphical User Interface(GUI), with a built-in text editor.
To execute the model ﬁle chess.mos you need to carry out the following steps.
Start up Workbench.
Open the model ﬁle by choosing File  Open. The model source is then displayed in the central
window (the Workbench Editor).
at the top of the window, making sure that the desired ﬁlename is
Click the Run button
selected in the input ﬁeld to its left, or alternatively, choose Run  Run chess.mos.

The resulting screen display is shown in Figure 1.1.

Figure 1.1: Xpress Workbench screen after running chess.mos

The logging pane at the bottom of the workspace is automatically displayed when compilation starts. If
syntax errors are found in the model, they are displayed here, with details of the line and character
position where the error was detected and a description of the problem, if available. If the model has
been compiled successfully, this pane displays the output produced by running the model.
If the model is run in debug mode by selecting the Debug button
Workbench makes all information
about the solution available through the Debugger pane on the right border of the workspace window.
By expanding the Variables entry in this pane, the solution and reduced cost values for decisionvariables are displayed. Dual and slack values for constraints may also be obtained.

Fair Isaac Corporation Conﬁdential and Proprietary Information

9

Getting started with Mosel

Figure 1.2: Running chess.mos with Xpress Workbench in debug mode

Fair Isaac Corporation Conﬁdential and Proprietary Information10

CHAPTER 2

Some illustrative examples
This chapter develops the basics of modeling set out in Chapter 1. It presents some further examplesof the use of Mosel and introduces new features:




2.1

Use of subscripts: Almost all models of any size have subscripted variables. We show how todeﬁne arrays of data and decision variables, introduce the different types of sets that may be
used as index sets for these arrays, and also simple loops over these sets.
Working with data ﬁles: Mosel provides facilities to read from and write to data ﬁles in text
format and also from other data sources (databases and spreadsheets).

The burglar problem
A burglar sees 8 items, of different worths and weights. He wants to take the items of greatest total
value whose total weight is not more than the maximum WTMAX he can carry.

2.1.1

Model formulation
We introduce binary variables takei for all i in the set of all items (ITEMS) to represent the decision
whether item i is taken or not. takei has the value 1 if item i is taken and 0 otherwise. Furthermore, let
VALUEi be the value of item i and WEIGHTi its weight. A mathematical formulation of the problem isthen given by:
X
maximize
VALUEi · takeii∈ITEMSX

WEIGHTi · takei ≤ WTMAX (weight restriction)i∈ITEMS∀i ∈ ITEMS : takei ∈ {0, 1}
The objective function is to maximize the total value, that is, the sum of the values of all items taken.
The only constraint in this problem is the weight restriction. This problem is an example of a knapsackproblem.

2.1.2

Implementation
It may be implemented with Mosel as follows (model ﬁle burglar.mos):model Burglar
uses "mmxprs"
declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information

11

Some illustrative examples
WTMAX = 102
ITEMS = 1..8

! Maximum weight allowed! Index range for items

VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of real

! Value of items! Weight of items

take: array(ITEMS) of mpvar
end-declarations! 1 if we take item i; 0 otherwise! Item:
1
2
3
4
5
6
7
8
VALUE :: [15, 100, 90, 60, 40, 15, 10, 1]
WEIGHT:: [ 2, 20, 20, 30, 40, 30, 60, 10]! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
maximize(MaxVal)! Solve the MIP-problem! Print out the solution
writeln("Solution:\n Objective: ", getobjval)
forall(i in ITEMS) writeln(" take(", i, "): ", getsol(take(i)))
end-model

When running this model we get the following output:
Solution:Objective: 280
take(1): 1
take(2): 1
take(3): 1
take(4): 1
take(5): 0
take(6): 1
take(7): 0
take(8): 0

In this model there are a lot of new features, which we shall now explain.
Constants:
WTMAX=102

declares a constant called WTMAX, and gives it the value 102. Since 102 is an integer, WTMAX is an
integer constant. Anything that is given a value in a declarations block is a constant.
Ranges:ITEMS = 1..8deﬁnes a range set, that is, a set of consecutive integers from 1 to 8. This range is used as anindex set for the data arrays (VALUE and WEIGHT) and for the array of decision variables take.


Arrays:VALUE: array(ITEMS) of realdeﬁnes a one-dimensional array of real values indexed by the range ITEMS. Exactly equivalentwould beFair Isaac Corporation Conﬁdential and Proprietary Information

12

Some illustrative examples
VALUE: array(1..8) of real

! Value of items

Multi-dimensional arrays are declared in the obvious way e.g.
VAL3: array(ITEMS, 1..20, ITEMS) of realdeclares a 3-dimensional real array. Arrays of decision variables (type mpvar) are declared
likewise, as shown in our example:
x: array(ITEMS) of mpvar

declares an array of decision variables take(1), take(2), ..., take(8).
All objects (scalars and arrays) declared in Mosel are always initialized with a default value:
real, integer: 0
boolean: false
string: ’’ (i.e. the empty string)
In Mosel, reals are double precision.
Assigning values to arrays:
The values of data arrays may either be deﬁned in the model as we show in the example orinitialized from ﬁle (see Section 2.2).
VALUE :: [15, 100, 90, 60, 40, 15, 10,1]ﬁlls the VALUE array as follows:
VALUE(1) gets the value 15; VALUE(2) gets the value 100; ..., VALUE(8) gets the value 1.
For a 2-dimensional array such as
declarations
EE: array(1..2, 1..3) of real
end-declarations

we might write
EE:: [11, 12, 13,
21, 22, 23 ]which of course is the same as
EE:: [11, 12, 13, 21, 22, 23]

but much more intuitive. Mosel places the values in the tuple into EE ‘going across the rows’, withthe last subscript varying most rapidly. For higher dimensions, the principle is the same. If theindex sets of an array are other than ranges they must be given when initializing the array withdata, in the case of ranges this is optional. Equivalently to the above we may write
VALUE :: (ITEMS)[15, 100, 90, 60, 40, 15, 10,
EE:: (1..2, 1..3)[11, 12, 13,21, 22, 23 ]1]

or even initialize the two-dimensional array EE rowwise:
EE:: (1, 1..3)[11, 12, 13]
EE:: (2, 1..3)[21, 22, 23 ]Summations:
MaxVal:= sum(i in Items) VALUE(i)⁎x(i)Fair Isaac Corporation Conﬁdential and Proprietary Information

13

Some illustrative examples

deﬁnes a linear expression called MaxVal as the sum
X
VALUEi · xii∈ItemsNaming constraints:
Optionally, constraints may be named (as in the chess set example). In the remainder of thismanual, we shall name constraints only if we need to refer to them at other places in the model.
In most examples, only the objective function is named (here MaxVal) — to be able to refer to it in
the call to the optimization (here maximize(MaxVal)).



Simple looping:
forall(i in ITEMS) take(i) is_binary

illustrates looping over all values in an index range. Recall that the index range ITEMS is 1, ..., 8, so
the statement says that take(1), take(2), ..., take(8) are all binary variables.
There is another example of the use of forall at the penultimate line of the model when writingout all the solution values.
Integer Programming variable types:
To make an mpvar variable, say variable xbinvar, into a binary (0/1) variable, we just have to say
xbinvar is_binary

To make an mpvar variable an integer variable, i.e. one that can only take on integral values in a
MIP problem, we would havexintvar is_integer

2.1.3

The burglar problem revisited
Consider this model (burglari.mos):model "Burglar (index set)"uses "mmxprs"
declarations
WTMAX = 102! Maximum weight allowed
ITEMS = {"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}! Index set for items
VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of real

! Value of items! Weight of items

take: array(ITEMS) of mpvar
end-declarations! 1 if we take item i; 0 otherwise

VALUE("camera") := 15;
VALUE("necklace"):=100;
VALUE("vase"):= 90;VALUE("picture") := 60;
VALUE("tv"):= 40;VALUE("video"):= 15;VALUE("chest"):= 10;VALUE("brick"):= 1;

WEIGHT("camera") := 2
WEIGHT("necklace"):= 20
WEIGHT("vase"):= 20
WEIGHT("picture") := 30
WEIGHT("tv"):= 40
WEIGHT("video"):= 30
WEIGHT("chest"):= 60
WEIGHT("brick"):= 10! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction

Fair Isaac Corporation Conﬁdential and Proprietary Information

14

Some illustrative examples
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
maximize(MaxVal)! Solve the MIP-problem! Print out the solution
writeln("Solution:\n Objective: ", getobjval)
forall(i in ITEMS) writeln(" take(", i, "): ", getsol(take(i)))
end-model

What have we changed? The answer is, ‘not very much’.
String indices:
ITEMS={"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}declares that this time ITEMS is a set of strings. The indices now take the string values ‘camera’,‘necklace’ etc. Since string index sets have no ﬁxed ordering like the range set we have used in theﬁrst version of the model, we now need to initialize every data item separately, or alternatively,
write out the index sets when deﬁning the array values, such as
VALUE :: (["camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"])[15,100,90,60,40,15,10,1]
WEIGHT:: (["camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"])[2,20,20,30,40,30,60,10]

If we run the model, we get
Solution:Objective: 280
take(camera): 1
take(necklace): 1
take(vase): 1
take(picture): 1
take(tv): 0
take(video): 1
take(chest): 0
take(brick): 0Continuation lines:
Notice that the statement
ITEMS={"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}was spread over two lines. Mosel is smart enough to recognize that the statement is notcomplete, so it automatically tries to continue on the next line. If you wish to extend a single
statement to another line, just cut it after a symbol that implies a continuation, like an operator (+,-, <=, ...) or a comma (,) in order to warn the analyzer that the expression continues in thefollowing line(s). For example
ObjMax:= sum(i in Irange, j in Jrange) TAB(i,j) ⁎ x(i,j) +sum(i in Irange) TIB(i) ⁎ delta(i)+sum(j in Jrange) TUB(j) ⁎ phi(j)Conversely, it is possible to place several statements on a single line, separating them bysemicolons (like x1 <= 4; x2 >= 7).

Fair Isaac Corporation Conﬁdential and Proprietary Information

15

Some illustrative examples

2.2

A blending example

2.2.1

The model backgroundA mining company has two types of ore available: Ore 1 and Ore 2. The ores can be mixed in varying
proportions to produce a ﬁnal product of varying quality. For the product we are interested in, the ‘grade’(a measure of quality) of the ﬁnal product must lie between the speciﬁed limits of 4 and 5. It sells for
REV = £125 per ton. The costs of the two ores vary, as do their availabilities. The objective is tomaximize the total net proﬁt.

2.2.2

Model formulation
Denote the amounts of the ores to be used by use1 and use2 . Maximizing net proﬁt (i.e., sales revenue
less cost COSTo of raw material) gives us the objective function:
X(REV – COSTo ) · useoo∈ORESWe then have to ensure that the grade of the ﬁnal ore is within certain limits. Assuming the grades ofthe ores combine linearly, the grade of the ﬁnal product is:P
GRADEo · useoo∈ORESPo∈ORES useo
This must be greater than or equal to 4 so, cross-multiplying and collecting terms, we have theconstraint:X(GRADEo – 4) · useo ≥ 0o∈ORES

Similarly the grade must not exceed 5.
P

GRADEo · useoo∈ORESPo∈ORES useo

≤5

So we have the further constraint:X(5 – GRADEo ) · useo ≥ 0o∈ORES

Finally only non-negative quantities of ores can be used and there is a limit to the availability AVAILo ofeach of the ores. We model this with the constraints:∀o ∈ ORES : 0 ≤ useo ≤ AVAILo

2.2.3

Implementation
The above problem description sets out the relationships which exist between variables but contains
few explicit numbers. Focusing on relationships rather than ﬁgures makes the model much more
ﬂexible. In this example only the selling price REV and the upper/lower limits on the grade of the ﬁnal
product (MINGRADE and MAXGRADE) are ﬁxed.
Enter the following model into a ﬁle blend.mos.

Fair Isaac Corporation Conﬁdential and Proprietary Information

16

Some illustrative examples
model "Blend"uses "mmxprs"
declarations
REV = 125
MINGRADE = 4
MAXGRADE = 5
ORES = 1..2

! Unit revenue of product! Minimum permitted grade of product! Maximum permitted grade of product! Range of ores

COST: array(ORES) of real
AVAIL: array(ORES) of real
GRADE: array(ORES) of real

! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)use: array(ORES) of mpvar
end-declarations! Quantities of ores used

! Read data from file blend.dat
initializations from 'blend.dat'
COST
AVAIL
GRADE
end-initializations! Objective: maximize total profit
Profit:= sum(o in ORES) (REV-COST(o))⁎ use(o)! Lower and upper bounds on ore quality
sum(o in ORES) (GRADE(o)-MINGRADE)⁎use(o) >= 0sum(o in ORES) (MAXGRADE-GRADE(o))⁎use(o) >= 0
! Set upper bounds on variables (lower bound 0 is implicit)
forall(o in ORES) use(o) <= AVAIL(o)maximize(Profit)! Solve the LP-problem! Print out the solution
writeln("Solution:\n Objective: ", getobjval)
forall(o in ORES) writeln(" use(" + o + "): ", getsol(use(o)))
end-model

The ﬁle blend.dat contains the following:! Data file for 'blend.mos'
COST: [85 93]
AVAIL: [60 45]
GRADE: [2.1 6.3]

The initializations from / end-initializations block is new here, telling Mosel where to
get data from to initialize named arrays. The order of the data items in the ﬁle does not have to be thesame as that in the initializations block; equally acceptable would have been the statements
initializations from 'blend.dat'
AVAIL GRADE COST
end-initializations

Alternatively, since all data arrays have the same indices, they may be given in the form of a single
record, such as BLENDDATA in the following data ﬁle blendb.dat:!
[COST AVAIL GRADE]
BLENDDATA: [ [85
60
2.1]
[93
45
6.3] ]In the initializations block we need to indicate the label of the data record and in which order thedata of the three arrays is given:

Fair Isaac Corporation Conﬁdential and Proprietary Information17

Some illustrative examples
initializations from 'blendb.dat'[COST,AVAIL,GRADE] as 'BLENDDATA'
end-initializations

2.2.4

Re-running the model with new data
There is a problem with the model we have just presented — the name of the ﬁle containing the costs
date is hard-wired into the model. If we wanted to use a different ﬁle, say blend2.dat, then we would
have to edit the model, and recompile it.
Mosel has parameters to help with this situation. A model parameter is a symbol, the value of which
can be set just before running the model, often as an argument of the run command of the commandline interpreter.
model "Blend 2"
uses "mmxprs"
parameters
DATAFILE="blend.dat"
end-parameters
declarations
REV = 125
MINGRADE = 4
MAXGRADE = 5
ORES = 1..2

! Unit revenue of product! Minimum permitted grade of product! Maximum permitted grade of product! Range of ores

COST: array(ORES) of real
AVAIL: array(ORES) of real
GRADE: array(ORES) of real

! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)use: array(ORES) of mpvar
end-declarations! Quantities of ores used

! Read data from fileinitializations from DATAFILE
COST
AVAIL
GRADE
end-initializations...
end-model

The parameter DATAFILE is recognized as a string, and its default value is speciﬁed. If we havepreviously compiled the model into say blend2.bim, then the command
mosel run blend2 DATAFILE="blend2.dat"

will read the cost data from the ﬁle we want. Or to compile, load, and run the model using a single
command:mosel exec blend2 DATAFILE="blend2.dat"

Notice that a model only takes a single parameters block that must follow immediately after theuses statement(s) at the beginning of the model.

2.2.5

Reading data from spreadsheets and databases
It is quite easy to create and maintain data tables in text ﬁles but in many industrial applications data
are provided in the form of spreadsheets or need to be extracted from databases. So there is a facility

Fair Isaac Corporation Conﬁdential and Proprietary Information18

Some illustrative examples

in Mosel whereby the contents of ranges within spreadsheets may be read into data tables anddatabases may be accessed.
In addition to the documentation of the Mosel modules mmodbc and mmsheet in the Mosel language
reference manual, you will ﬁnd further detail and examples of using the SQL/ODBC and spreadsheet
interfaces in other documents of the Xpress distribution: the whitepaper Using ODBC and otherdatabase interfaces with Mosel explains how to set up an ODBC connection and discusses a large
number of examples showing different SQL/ODBC features; the whitepaper Generalized ﬁle handling in
Mosel also contains several examples of the use of ODBC. To give you a ﬂavor of how Mosel’s ODBC
and spreadsheet interfaces may be used, we now read the data of the blending problem from a
spreadsheet and then later from a database.
The ODBC technology is a generic means for accessing databases and some spreadsheets such as
certain versions of Microsoft Excel also support (a reduced set of) ODBC functionality. Mosel also
provides a speciﬁc interface to Excel spreadsheets, an example of which is shown below (Section
2.2.5.1). This interface that supports all basic tasks of data exchange should be used for working with
Excel data. A generic alternative for working with spreadsheets in .xls, .xlsx, or .csv format, including on
non-Windows platforms, is discussed in Section 2.2.5.3.
2.2.5.1

Excel spreadsheets

Let us suppose that in a Microsoft Excel spreadsheet called blend.xls you have inserted thefollowing into the cells indicated:
Table 2.1: Spreadsheet example data
A

B

C

D

E

2

ORES

COST

AVAIL

GRADE

3

1

85

60

2.1

4

2

93

45

6.3

F

1

5

and called the range B3:E4 MyRange.
The following model reads the data for the arrays COST, AVAIL, and GRADE from the Excel range
MyRange. Note that we have added "mmsheet" to the uses statement to indicate that we are using
the Mosel spreadsheet module.
model "Blend 3"
uses "mmxprs", "mmsheet"declarations
REV = 125
MINGRADE = 4
MAXGRADE = 5
ORES = 1..2

! Unit revenue of product! Minimum permitted grade of product! Maximum permitted grade of product! Range of ores

COST: array(ORES) of real
AVAIL: array(ORES) of real
GRADE: array(ORES) of real

! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)use: array(ORES) of mpvar
end-declarations! Quantities of ores used

! Read data from spreadsheet blend.xls
initializations from "mmsheet.excel:blend.xls"[COST,AVAIL,GRADE] as "MyRange"
end-initializations...

Fair Isaac Corporation Conﬁdential and Proprietary Information

19

Some illustrative examples

end-model

Instead of naming the ranges in the spreadsheet it is equally possible to work directly with the cellreferences (including the worksheet name, that is, ‘Sheet1’ in our case):
initializations from "mmsheet.excel:blend.xls"[COST,AVAIL,GRADE] as "[Sheet1$B3:E4]"
end-initializations

or alternatively, work with row and column counters:
initializations from "mmsheet.excel:blend.xls"[COST,AVAIL,GRADE] as "[Sheet1$R3C2:R4C5]"
end-initializations

And we can also select speciﬁc columns from a range:
initializations from "mmsheet.excel:blend.xls"
GRADE as "MyRange(#1,#4)"
end-initializations

If the range deﬁnition contains the header line with column titles (so, MyRangeWithHeader is the area
B2:E4) we can also select speciﬁc columns via their titles:
initializations from "mmsheet.excel:blend.xls"
GRADE as "skiph;MyRangeWithHeader(ORES,GRADE)"
end-initializations

2.2.5.2

Database example

If we use Microsoft Access, we might have set up an ODBC DSN called MSAccess. NB: this is where tocheck whether the DSN is set up with Windows 2000 or XP: Start  Settings  Control Panel Administrative Tools  Data Sources (ODBC)  ODBC drivers.
Suppose we are extracting data from a table called MyTable in the database blend.mdb. There arejust the four columns ORES, COST, AVAIL, and GRADE in MyTable, and the data are the same as in the
Excel example above. We modify the example above to bemodel "Blend 4"
uses "mmxprs", "mmodbc"
declarations
REV = 125
MINGRADE = 4
MAXGRADE = 5
ORES = 1..2

! Unit revenue of product! Minimum permitted grade of product! Maximum permitted grade of product! Range of ores

COST: array(ORES) of real
AVAIL: array(ORES) of real
GRADE: array(ORES) of real

! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)use: array(ORES) of mpvar
end-declarations! Quantities of ores used

! Read data from database blend.mdb
initializations from "mmodbc.odbc:blend.mdb"[COST,AVAIL,GRADE] as "MyTable"
end-initializations...
end-model

Fair Isaac Corporation Conﬁdential and Proprietary Information

20

Some illustrative examples

With ODBC, we can use the ﬁeld names to select speciﬁc columns from a table:
initializations from "mmodbc.odbc:blend.mdb"
GRADE as "MyTable(ORES,GRADE)"
end-initializations

Instead of using the initializations block that automatically generates SQL commands forreading and writing data it is also possible to employ SQL statements in Mosel models. The
initializations block in the model above is equivalent to the following sequence of SQL
statements:
SQLconnect('DSN=MSAccess; DBQ=blend.mdb')
SQLexecute("select ⁎ from MyTable ", [COST,AVAIL,GRADE])
SQLdisconnect

The SQL statement "select ⁎ from MyTable" says ‘select everything from the table called
MyTable’. By using SQL statements directly in the Mosel model it is possible to have much more
complex selection statements than the ones we have used.
To use other databases, for instance a mysql database (let us call it blend), we merely need to modify
the connection string — provided that we have given the same names to the data table and its columns:
initializations from "mmodbc.odbc:DSN=mysql;DB=blend"

ODBC, just like Mosel’s text ﬁle format, may also be used to output data. The reader is referred to the
ODBC/SQL documentation for more detail.
2.2.5.3

Generic spreadsheet example

We shall work once more with the Microsoft Excel spreadsheet called blend.xls shown in Table 2.1
where we have deﬁned the range B3:E4 MyRange.
This spreadsheet can be accessed via MS Excel as shown above. However, this access method is only
available on platforms where Excel is installed. The module mmsheet also provides more genericinterfaces for working with .xsl, .xlsx and CSV format ﬁles (usable, for example, under Linux or MacOS).
The corresponding Mosel model looks as follows.
model "Blend 3 (spreadsheet)"uses "mmsheet", "mmxprs"
declarations
REV = 125
MINGRADE = 4
MAXGRADE = 5
ORES = 1..2

! Unit revenue of product! Minimum permitted grade of product! Maximum permitted grade of product! Range of ores

COST: array(ORES) of real
AVAIL: array(ORES) of real
GRADE: array(ORES) of real

! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)use: array(ORES) of mpvar
end-declarations! Quantities of ores used

! Read data from spreadsheet blend.xls
initializations from "mmsheet.xls:blend.xls"[COST,AVAIL,GRADE] as "MyRange"
end-initializations...
end-model

Fair Isaac Corporation Conﬁdential and Proprietary Information

21

Some illustrative examples

The only modiﬁcation we have made is quite subtle: in the ﬁlename we have replaced mmsheet.excel
by mmsheet.xls.
Variant: Assuming that we have saved the data from our spreadsheet into the CSV format ﬁleblend.csv, we need to switch to the CSV interface for accessing this data ﬁle. A CSV ﬁle contains asingle worksheet and it is not possible to deﬁne named ranges. We therefore now refer directly to thecells via the cell references (similarly to what has been shown for Excel in Section 2.2.5.1 but without
stating a sheet name):
initializations from "mmsheet.csv:blend.csv"[COST,AVAIL,GRADE] as "[B3:E4]"
end-initializations

or alternatively, using row and column counters:
initializations from "mmsheet.csv:blend.csv"[COST,AVAIL,GRADE] as "[R3C2:R4C5]"
end-initializations

Fair Isaac Corporation Conﬁdential and Proprietary Information

22

CHAPTER 3

More advanced modeling features
3.1

Overview
This chapter introduces some more advanced features of the modeling language in Mosel. We shallnot attempt to cover all its features or give the detailed speciﬁcation of their formats. These arecovered in greater depth in the Mosel Reference Manual.
Almost all large scale LP and MIP problems have a property known as sparsity, that is, each variableappears with a non-zero coefﬁcient in a very small fraction of the total set of constraints. Often thisproperty is reﬂected in the data tables used in the model in that many values of the tables are zero.
When this happens, it is more convenient to provide just the non-zero values of the data table rather
than listing all the values, the majority of which are zero. This is also the easiest way to input data into
data tables with more than two dimensions. An added advantage is that less memory is used by Mosel.
The main areas covered in this chapter are related to this property:dynamic arrays



sparse data

conditional generationdisplaying data

We start again with an example problem. The following sections deal with the different topics in moredetail.

3.2

A transport example
A company produces the same product at different plants in the UK. Every plant has a different
production cost per unit and a limited total capacity. The customers (grouped into customer regions)
may receive the product from different production locations. The transport cost is proportional to the
distance between plants and customers, and the capacity on every delivery route is limited. The
objective is to minimize the total cost, whilst satisfying the demands of all customers.

3.2.1

Model formulation
Let PLANT be the set of plants and REGION the set of customer regions. We deﬁne decision variables
ﬂowpr for the quantity transported from plant p to customer region r. The total cost of the amount ofFair Isaac Corporation Conﬁdential and Proprietary Information

23

More advanced modeling features

product p delivered to region r is given as the sum of the transport cost (the distance between p and r
multiplied by a factor FUELCOST) and the production cost at plant p:
X
X(FUELCOST · DISTANCEpr + PLANTCOSTp ) · ﬂowpr
minimizep∈PLANT r∈REGION

The limits on plant capacity are give through the constraints
X
ﬂowpr ≤ PLANTCAPp∀p ∈ PLANT :r∈REGION

We want to meet all customer demands:X∀r ∈ REGION :ﬂowpr = DEMANDrp∈PLANTThe transport capacities on all routes are limited and there are no negative ﬂows:∀p ∈ PLANT, r ∈ REGION : 0 ≤ ﬂowpr ≤ TRANSCAPpr
For simplicity’s sake, in this mathematical model we assume that all routes p → r are deﬁned and that
we have TRANSCAPpr = 0 to indicate that a route cannot be used.

3.2.2

Implementation
This problem may be implemented with Mosel as shown in the following (model ﬁle transport.mos):model Transport
uses "mmxprs"
declarations
REGION: set of string
PLANT: set of string! Set of customer regions! Set of plants

DEMAND: array(REGION) of real
! Demand at regions
PLANTCAP: array(PLANT) of real
! Production capacity at plants
PLANTCOST: array(PLANT) of real
! Unit production cost at plants
TRANSCAP: dynamic array(PLANT,REGION) of real
! Capacity on each route plant->region
DISTANCE: dynamic array(PLANT,REGION) of real
! Distance of each route plant->region
FUELCOST: real
! Fuel cost per unit distance
flow: dynamic array(PLANT,REGION) of mpvar
end-declarations! Flow on each routeinitializations from 'transprt.dat'
DEMAND[PLANTCAP,PLANTCOST] as 'PLANTDATA'[DISTANCE,TRANSCAP] as 'ROUTES'FUELCOST
end-initializations! Create the flow variables that exist
forall(p in PLANT, r in REGION | exists(TRANSCAP(p,r)) ) create(flow(p,r))! Objective: minimize total cost
MinCost:= sum(p in PLANT, r in REGION | exists(flow(p,r)))(FUELCOST ⁎ DISTANCE(p,r) + PLANTCOST(p)) ⁎ flow(p,r)! Limits on plant capacity

Fair Isaac Corporation Conﬁdential and Proprietary Information

24

More advanced modeling features
forall(p in PLANT) sum(r in REGION) flow(p,r) <= PLANTCAP(p)</p>! Satisfy all demands
forall(r in REGION) sum(p in PLANT) flow(p,r) = DEMAND(r)! Bounds on flows
forall(p in PLANT, r in REGION | exists(flow(p,r)))flow(p,r) <= TRANSCAP(p,r)
minimize(MinCost)! Solve the problem

end-model

REGION and PLANT are declared to be sets of strings, as yet of unknown size. The data arrays(DEMAND, PLANTCAP, PLANTCOST, TRANSCAP, and DISTANCE) and the array of variables flow areindexed by members of REGION and PLANT, their size is therefore not known at their declaration. The
model shows two forms of such array declarations: (1) the arrays DEMAND, PLANTCAP, PLANTCOST aredense arrays that are not ﬁxed (all entries corresponding to their index sets exist, new entries are added
via assignment or if their index sets grow), (2) the arrays TRANSCAP, DISTANCE), and flow are marked
as dynamic, that is, only explicitly assigned or created entries exist — we want to make use of thisproperty in the formulation of the model.
There is a slight difference between dynamic arrays of data and of decision variables (type mpvar): an
entry of a data array is created automatically when it is used in the Mosel program, entries of decisionvariable arrays need to be created explicitly (see Section 3.3.1 below).
The data ﬁle transprt.dat contains the problem speciﬁc data. It might have, for instance,
DEMAND: [ (Scotland) 2840 (North) 2800 (SWest) 2600 (SEast) 2820 (Midlands) 2750]! [CAP COST]
PLANTDATA: [ (Corby)[3000 1700](Deeside) [2700 1600](Glasgow) [4500 2000](Oxford) [4000 2100] ]! [DIST CAP]ROUTES: [ (Corby
North)[400 1000](CorbySWest)[400 1000](CorbySEast)[300 1000](CorbyMidlands) [100 2000](Deeside Scotland) [500 1000](Deeside North)[200 2000](Deeside SWest)[200 1000](Deeside SEast)[200 1000](Deeside Midlands) [400 300]
(Glasgow Scotland) [200 3000](Glasgow North)[400 2000](Glasgow SWest)[500 1000](Glasgow SEast)[900 200](Oxford Scotland) [800⁎](Oxford North)[600 2000](Oxford SWest)[300 2000](Oxford SEast)[200 2000](Oxford Midlands) [400 500] ]FUELCOST: 17

where we give the ROUTES data only for possible plant/region routes, indexed by the plant and region. It
is possible that some data are not speciﬁed; for instance, there is no Corby – Scotland route. So thedata are sparse and we just create the ﬂow variables for the routes that exist. (The ‘⁎’ for the(Oxford,Scotland) entry in the capacity column indicates that the entry does not exist; we may write ’0’
instead: in this case the corresponding ﬂow variable will be created but bounded to be 0 by thetransport capacity limit).

Fair Isaac Corporation Conﬁdential and Proprietary Information

25

More advanced modeling features

The condition whether an entry in a data table is deﬁned is tested with the Mosel function exists.
With the help of the ‘|’ operator we add this test to the forall loop creating the variables. It is not
required to add this test to the sums over these variables: only the ﬂowpr variables that have beencreated are taken into account. However, if the sums involve exactly the index sets that have been usedin the declaration of the variables (here this is the case for the objective function MinCost), adding theexistence test helps to speed up the enumeration of the existing index-tuples. The following section
introduces the conditional generation in a more systematic way.

3.3

Conditional generation — the | operator
Suppose we wish to apply an upper bound to some but not all members of a set of variables xi . There
are MAXI members of the set. The upper bound to be applied to xi is Ui , but it is only to be applied if theentry in the data table TABi is greater than 20. If the bound did not depend on the value in TABi then thestatement would read:
forall(i in 1..MAXI) x(i) <= U(i)Requiring the condition leads us to write
forall(i in 1..MAXI | TAB(i) > 20 ) x(i) <= U(i)The symbol ‘|’ can be read as ‘such that’ or ‘subject to’.
Now suppose that we wish to model the following
MAXIX

xi ≤ 15i=1
Ai >20In other words, we just want to include in a sum those xi for which Ai is greater than 20. This isaccomplished by
CC:= sum((i in 1..MAXI | A(i)>20 ) x(i) <= 15

3.3.1

Conditional variable creation and create
As we have already seen in the transport example (Section 3.2), with Mosel we can conditionally create
variables. In this section we show a few more examples.
Suppose that we have a set of decision variables x(i) where we do not know the set of i for whichx(i) exist until we have read data into an array WHICH.
model doesx
public declarations
IR = 1..15
WHICH: set of integer
x: dynamic array(IR) of mpvar
Obj,C: linctr
end-declarations! Read data from fileinitializations from 'doesx.dat'
WHICH
end-initializations! Create the x variables that exist

Fair Isaac Corporation Conﬁdential and Proprietary Information

26

More advanced modeling features
forall(i in WHICH) create(x(i))! Build a little model to show what esists
Obj:= sum(i in IR) x(i)C:= sum(i in IR) i ⁎ x(i) >= 5
exportprob("", Obj)
end-model! Display the model

If the data in doesx.dat are
WHICH: [1 4 7 11 14]

the output from the model is
Minimizex(1) + x(4) + x(7) + x(11) + x(14)Subject To
C: x(1) + 4 x(4) + 7 x(7) + 11 x(11) + 14 x(14) >= 5
BoundsEnd

Note: exportprob("", Obj) is a nice idiom for seeing on-screen the problem that has beencreated. The public declaration of decision variables and constraints ensures that the display
employs the entity names from the model, by default it will only show automatically generated names.
The key point is that x has been declared as a dynamic array, and then the variables that exist havebeen created explicitly with create. In the transport example in Section 3.2 we have seen a different
way of declaring dynamic arrays: the arrays are implicitly declared as dynamic arrays since the index
sets are unknown at their declaration.
When we later take operations over the index set of x (for instance, summing), we only include those xthat have been created.
Another way to do this, ismodel doesx2
public declarations
WHICH: set of integer
Obj,C: linctr
end-declarations
initializations from 'doesx.dat'
WHICH
end-initializations
finalize(WHICH)
public declarations
x: array(WHICH) of mpvar
end-declarations! Here the array is _not_ dynamic! because the set has been finalized

Obj:= sum(i in WHICH) x(i)C:= sum(i in WHICH) i ⁎ x(i) >= 5
exportprob(0, "", Obj)
end-model

By default, an array is of ﬁxed size if all of its indexing sets are of ﬁxed size (i.e. they are either constantor have been ﬁnalized). Finalizing turns a dynamic set into a constant set consisting of the elementsthat are currently in the set. All subsequently declared arrays that are indexed by this set will be createdas static (= ﬁxed size). The second method has two advantages: it is more efﬁcient, and it does not
require us to think of the limits of the range IR a priori.

Fair Isaac Corporation Conﬁdential and Proprietary Information

27

More advanced modeling features

Note: The explicit call to finalize has become optional with Mosel 3.0 as the automatic ﬁnalization
mechanism of Mosel performs this operation by default.

3.4

Reading sparse data
Suppose we want to read in data of the formi, j, valueij

from an ASCII ﬁle, setting up a dynamic array A(range, range) with just the A(i,j)= valueij forthe pairs (i,j) which exist in the ﬁle. Here is an example which shows three different ways of doing
this. We read data from differently formatted ﬁles into three different arrays, and using writeln showthat the arrays hold identical data.

3.4.1

Data input with initializations from
The ﬁrst method, using the initializations block, has already been introduced (transport problem
in Section 3.2).
model "Trio input (1)"declarations
A1: dynamic array(range,range) of real
end-declarations! First method: use an initializations block
initializations from 'data_1.dat'
A1 as 'MYDATA'
end-initializations! Now let us see what we havewriteln('A1 is: ', A1)
end-model

The data ﬁle data_1.dat could be set up thus (every data item is preceded by its index-tuple):
MYDATA: [ (1 1) 12.5 (2 3) 5.6 (10 9) -7.1 (3 2) 1 ]This model produces the following output:
A1 is: [(1,1,12.5),(2,3,5.6),(3,2,1),(10,9,-7.1)]

3.4.2

Data input with readln
The second way of setting up and accessing data demonstrates the immense ﬂexibility of readln.
The format of the data ﬁle may be freely deﬁned by the user. After every call to read or readln theparameter nbread contains the number of items read. Its value should be tested to check whether theend of the data ﬁle has been reached or an error has occurred (e.g. unrecognized data items due toincorrect formating of a data line). Notice that read and readlninterpret spaces as separators
between data items; strings containing spaces must therefore be quoted using either single or double
quotes.
model "Trio input (2)"declarations
A2: dynamic array(range,range) of reali, j: integer
end-declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information

28

More advanced modeling features! Second method: use the built-in readln function
fopen("data_2.dat",F_INPUT)
repeatreadln('Tut(', i, 'and', j, ')=', A2(i,j))
until getparam("nbread") < 6
fclose(F_INPUT)! Now let us see what we havewriteln('A2 is: ', A2)
end-model

The data ﬁle data_2.dat could be set up thus:
File data_2.dat:
Tut(1 and 1)=12.5
Tut(2 and 3)=5.6
Tut(10 and 9)=-7.1
Tut(3 and 2)=1

When running this second model version we get the same output as before:
A2 is: [(1,1,12.5),(2,3,5.6),(3,2,1),(10,9,-7.1)]

3.4.3

Data input with diskdata
As a third possibility, one may use the diskdata I/O driver from module mmetc to read in comma
separated value (CSV) ﬁles. With this driver the data ﬁle may contain single line comments precededwith !.
model "Trio input (3)"uses "mmetc"! Required for diskdata

declarations
A3: dynamic array(range,range) of real
end-declarations! Third method: use diskdata driverinitializations from 'mmetc.diskdata:'
A3 as 'sparse,data_3.dat'
end-initializations! Now let us see what we havewriteln('A3 is: ', A3)
end-model

The data ﬁle data_3.dat is set up thus (one data item per line, preceded by its indices, all separated
by commas; strings should be quoted using either single or double quotes):
1, 1, 12.5
2, 3, 5.6
10,9, -7.1
3, 2, 1

We obtain again the same output as before when running this model version:
A3 is: [(1,1,12.5),(2,3,5.6),(3,2,1),(10,9,-7.1)]

Note: the diskdata format is deprecated, it is provided to enable the use of data sets designed for
mp-model and does not support certain new features introduced by Mosel.

Fair Isaac Corporation Conﬁdential and Proprietary Information

29

More advanced modeling features

3.5

I/O error handling
Mosel’s default behaviour on encountering an error is to output an error message and exit from model
execution. If a model is embedded into an application this behaviour might not always be desirable,
particularly in the case of I/O errors. Data ﬁlenames (and contents) most often are changed at runtime
and they are therefore relatively more error-prone than invariable parts of the application.
The following modiﬁed extract of the ’transport’ example from Section 3.2 shows how to implement
custom I/O error handling in a Mosel model. To override the default error handling, this example uses
getparam and setparam to access and change the settings of several Mosel parameters:
ioctrlEnable/disable user I/O handling. If disabled (default), the model stops when an I/Oerror has occurred.

readcnt

Enable/disable counting of entries per label in ’initializations’ blocks. Needs to beenabled when using function getreadcnt.

nbreadNumber of items recognized by the last read procedure or read in by the last
’initializations’ block.

iostatus

Status of the last I/O operation. A non-zero value indicates an error.

workdir

The current working directory of the model. Data ﬁles are searched for relative to themodel’s working directory—incorrect paths are quite a common source of I/O errors.

Furthermore, we use the function getfstat provided by the module mmsystem to check whether thedata ﬁle we are about to access exists and is of a suitable type (regular ﬁle).
Model ﬁle readdataerr.mos:model "I/O error handling"uses "mmsystem"
declarations
REGION: set of string! Set of customer regions
PLANT: set of string! Set of plants
DEMAND: array(REGION) of real
! Demand at regions
TRANSCAP,DISTANCE: dynamic array(PLANT,REGION) of real
! Route data
FUELCOST: real
! Fuel cost per unit distanceend-declarations
DATAFILE:= 'transprt.dat'! Check whether the file we want to access exists
if bittest(getfstat(DATAFILE),SYS_TYP)<>SYS_REG thenwriteln("File '", DATAFILE, "' does not exist or is not a regular file")exit(1)
end-ifsetparam("ioctrl", true)
setparam("readcnt", true)! Application handles I/O errors! Enable per label counting

initializations from DATAFILE
DEMAND[DISTANCE,TRANSCAP] as 'ROUTE'
FUELCOST
end-initializations
if getparam("iostatus") <>0 then! Something has gone wrong in last I/Owriteln("I/O error reading file '", DATAFILE, "'.")! Display the working directory
writeln("Working directory: ", getparam("workdir"))! Display total entries read

Fair Isaac Corporation Conﬁdential and Proprietary Information

30

More advanced modeling features
writeln("Total number of entries read: ", getparam("nbread"))! Check no. of entries read per label
forall(s in ["DEMAND","ROUTE","FUELCOST"])
if getreadcnt(s)=0 thenwriteln("No entries read for label '", s, "'.")
else
writeln(getreadcnt(s), " entries read for label '", s, "'.")
end-ifend-ifsetparam("ioctrl", false)
setparam("readcnt", false)! Revert to default I/O handlingend-model

We have purposely introduced a mistake (the correct label for the route data is ’ROUTES’) and running
this model therefore displays an error message produced by Mosel, and also the following output
produced by our own error reporting.
I/O error reading file 'transprt.dat':
Mosel: E-33: Initialization from file `transprt.dat' failed for: `ROUTE'.
Working directory: c:/xpress/examples/mosel/UG/A3
Total number of entries read: 6
5 entries read for label 'DEMAND'.
No entries read for label 'ROUTE'.
1 entries read for label 'FUELCOST'.

Given that this model implements its own error handling, we might want to entirely disable the displayof error messages from Mosel by redirecting the error stream to ’null:’, that is, surrounding the’initializations’ block with these lines:
fopen("null:", F_ERROR)...
fclose(F_ERROR)! Optional: Disable error stream! Initialization of data from file
! Stop error redirection

Important: always remember to terminate the error stream redirection by closing the selected output
ﬁle, otherwise you will no longer see any error output from Mosel from the rest of the model.
Instead of completely ignoring the error messages produced by Mosel, we might also choose to savethem to a ﬁle in order to inspect or display them later on. This may be a physical (text) ﬁle, or for
example, a text object directly in the model as shown in this code extract:public declarations
errtxt: text
end-declarations! Text used as file to log errors

fopen("text:errtxt", F_ERROR)...
fclose(F_ERROR)! Redirect error stream to a file (text)! Initialization of data from file
! Stop error redirection

if getparam("iostatus") <>0 then! Something has gone wrong in last I/Owriteln("I/O error reading file '", DATAFILE, "': ", errtxt)...
end-ifIn the error redirection we have used ’null:’ and ’text:’, these two are I/O drivers which are explained withsome more detail in Section 17.1.2. Concerning the type ’text’ please see the discussion in Section
17.6.1.
Note: Certain Mosel modules and also the Mosel Libraries have additional functionality for error
handling, such as debug settings for ODBC (see the chapter ’mmodbc’ of the Mosel Language
Reference for details), or the redirection of Mosel streams from applications (as in Sections 13.5 or
14.1.8) of other models (see the example of Section 17.2.3).

Fair Isaac Corporation Conﬁdential and Proprietary Information

31

CHAPTER 4

Integer Programming
Though many systems can accurately be modeled as Linear Programs, there are situations where
discontinuities are at the very core of the decision making problem. There seem to be three major areas
where non-linear facilities are required

where entities must inherently be selected from a discrete set;in modeling logical conditions; andin ﬁnding the global optimum over functions.

Mosel lets you model these non-linearities using a range of discrete (global) entities and then the
Xpress Mixed Integer Programming (MIP) optimizer can be used to ﬁnd the overall (global) optimum of
the problem. Usually the underlying structure is that of a Linear Program, but optimization may be usedsuccessfully when the non-linearities are separable into functions of just a few variables.

4.1

Integer Programming entities in Mosel
We shall show how to make variables and sets of variables into global entities by using the following
declarations.
declarations
IR = 1..8
WEIGHT: array(IR) of real
x: array(IR) of mpvar
end-declarations! Index range! Weight table

WEIGHT:: [ 2, 5, 7, 10, 14, 18, 22, 30]

Xpress handles the following global entities:

Binary variables: decision variables that can take either the value 0 or the value 1 (do/ don’t dovariables).
We make a variable, say x(4), binary byx(4) is_binaryInteger variables: decision variables that can take only integer values.
We make a variable, say x(7), integer byx(7) is_integerPartial integer variables: decision variables that can take integer values up to a speciﬁed limit andany value above that limit.

Fair Isaac Corporation Conﬁdential and Proprietary Information

32

Integer Programmingx(1) is_partint 5
Semi-continuous variables: decision variables that can take either the value 0, or a value between
some lower limit and upper limit. Semi-continuous variables help model situations where if avariable is to be used at all, it has to be used at some minimum level.
x(2) is_semcont 6! A 'hole' between 0 and 6, then continuous

Semi-continuous integer variables: decision variables that can take either the value 0, or an integer
value between some lower limit and upper limit. Semi-continuous integer variables help modelsituations where if a variable is to be used at all, it has to be used at some minimum level, and has
to be integer.
x(3) is_semint 7

! Integer up to 5, then continuous! A 'hole' between 0 and 7, then integer

Special Ordered Sets of type one (SOS1): an ordered set of non-negative variables at most one ofwhich can take a non-zero value.
Special Ordered Sets of type two (SOS2): an ordered set of non-negative variables, of which at
most two can be non-zero, and if two are non-zero these must be consecutive in their ordering. If
the coefﬁcients in the WEIGHT array determine the ordering of the variables, we might form a
SOS1 or SOS2 set MYSOS by
MYSOS:= sum(i in IRng) WEIGHT(i)⁎x(i) is_sosX

where is_sosX is either is_sos1 for SOS1 sets, or is_sos2 for SOS2 sets.
Alternatively, if the set S holds the members of the set and the linear constraint L contains the set
variables’ coefﬁcients used in ordering the variables (the so-called reference row entries), then we
can do thus:
makesos1(S,L)

with the obvious change for SOS2 sets. This method must be used if the coefﬁcient (here
WEIGHT(i)) of an intended set member is zero. With is_sosX the variable will not appear in theset since it does not appear in the linear expression.
Another point to note about Special Ordered Sets is that the ordering coefﬁcients must be distinct(or else they are not doing their job of supplying an order!).
The most commonly used entities are binary variables, which can be employed to model a whole rangeof logical conditions. General integers are more frequently found where the underlying decision variable
really has to take on a whole number value for the optimal solution to make sense. For instance, wemight be considering the number of airplanes to charter, where fractions of an airplane are notmeaningful and the optimal answer will probably involve so few planes that rounding to the nearest
integer may not be satisfactory.
Partial integers provide some computational advantages in problems where it is acceptable to roundthe LP solution to an integer if the optimal value of a decision variable is quite large, but unacceptable if
it is small. Semi-continuous variables are useful where, if some variable is to be used, its value must be
no less than some minimum amount. If the variable is a semi-continuous integer variable, then it has
the added restriction that it must be integral too.
Special Ordered Sets of type 1 are often used in modeling choice problems, where we have to select at
most one thing from a set of items. The choice may be from such sets as: the time period in which tostart a job; one of a ﬁnite set of possible sizes for building a factory; which machine type to process apart on. Special Ordered Sets of type 2 are typically used to model non-linear functions of a variable.
They are the natural extension of the concepts of Separable Programming, but when embedded in a
Branch-and-Bound code (see below) enable truly global optima to be found, and not just local optima.
(A local optimum is a point where all the nearest neighbors are worse than it, but where we have no

Fair Isaac Corporation Conﬁdential and Proprietary Information

33

Integer Programming

guarantee that there is not a better point some way away. A global optimum is a point which we know
to be the best. In the Himalayas the summit of K2 is a local maximum height, whereas the summit ofEverest is the global maximum height).
Theoretically, models that can be built with any of the entities we have listed above can be modeled
solely with binary variables. The reason why modern IP systems have some or all of the extra entities isthat they often provide signiﬁcant computational savings in computer time and storage when trying tosolve the resulting model. Most books and courses on Integer Programming do not emphasize thispoint adequately. We have found that careful use of the non-binary global entities often yields very
considerable reductions in solution times over ones that just use binary variables.
To illustrate the use of Mosel in modeling Integer Programming problems, a small example follows. The
ﬁrst formulation uses binary variables. This formulation is then modiﬁed to use Special Ordered Sets.
For the interested reader, an excellent text on Integer Programming is Integer Programming by Laurence
Wolsey, Wiley Interscience, 1998, ISBN 0-471-28366-5.

4.2

A project planning model
A company has several projects that it must undertake in the next few months. Each project lasts for agiven time (its duration) and uses up one resource as soon as it starts. The resource proﬁle is theamount of the resource that is used in the months following the start of the project. For instance,
project 1 uses up 3 units of resource in the month it starts, 4 units in its second month, and 2 units in its
last month.
The problem is to decide when to start each project, subject to not using more of any resource in agiven month than is available. The beneﬁt from the project only starts to accrue when the project has
been completed, and then it accrues at BENp per month for project p, up to the end of the time horizon.
Below, we give a mathematical formulation of the above project planning problem, and then display theMosel model form.

4.2.1

Model formulation
Let PROJ denote the set of projects and MONTHS = {1, ..., NM} the set of months to plan for. The data
are:
DURp

the duration of project p

RESUSEpt

the resource usage of project p in its tth month

RESMAXmthe resource available in month m

BENp

the beneﬁt per month when project ﬁnishes

We introduce the binary decision variables startpm that are 1 if project p starts in month m, and 0
otherwise.
The objective function is obtained by noting that the beneﬁt coming from a project only starts to accrue
when the project has ﬁnished. If it starts in month m then it ﬁnishes in month m + DURp – 1. So, in total,
we get the beneﬁt of BENp for NM – (m + DURp – 1) = NM – m – DURp + 1 months. We must consider all
the possible projects, and all the starting months that let the project ﬁnish before the end of theplanning period. For the project to complete it must start no later than month NM – DURp . Thus the
proﬁt is:X NM–DUR
X p(BENp · (NM – m – DURp + 1)) · startpmp∈PROJ

m=1

Fair Isaac Corporation Conﬁdential and Proprietary Information

34

Integer Programming

Each project must be done once, so it must start in one of the months 1 to NM – DURp :X
startpm = 1∀p ∈ PROJ :m∈MONTHS

We next need to consider the implications of the limited resource availability each month. Note that if aproject p starts in month m it is in its (k – m + 1)th month in month k, and so will be using RESUSEp,k–m+1
units of the resource. Adding this up for all projects and all starting months up to and including theparticular month k under consideration gives:∀k ∈ MONTHS :k
X X

RESUSEp,k+1–m · startpm ≤ RESMAXkp∈PROJ m=1

Finally we have to specify that the startpm are binary (0 or 1) variables:∀p ∈ PROJ, m ∈ MONTHS : startpm ∈ {0, 1}
Note that the start month of a project p is given by:
NM–DURp

X

m · startpm

m=1

since if an startpm is 1 the summation picks up the corresponding m.

4.2.2

Implementation
The model as speciﬁed to Mosel is as follows (ﬁle pplan.mos):model Pplan
uses "mmxprs"
declarations
PROJ = 1..3
NM = 6MONTHS = 1..NM! Set of projects! Time horizon (months)! Set of time periods (months) to plan for

DUR: array(PROJ) of integer! Duration of project p
RESUSE: array(PROJ,MONTHS) of integer! Res. usage of proj. p in its t'th month
RESMAX: array(MONTHS) of integer ! Resource available in month m
BEN: array(PROJ) of real
! Benefit per month once project finishedstart: array(PROJ,MONTHS) of mpvar
end-declarations
DUR:: [3, 3, 4]
RESMAX:: [5, 6, 7, 7, 6, 6]
BEN:: [10.2, 12.3, 11.2]
RESUSE:: (1,1..3)[3, 4, 2]
RESUSE:: (2,1..3)[4, 1, 5]
RESUSE:: (3,1..4)[3, 2, 1, 2]! 1 if proj p starts in month t, else 0! Other RESUSE entries are 0 by default! Objective: Maximize Benefit! If project p starts in month t, it finishes in month t+DUR(p)-1 and! contributes a benefit of BEN(p) for the remaining NM-(t+DUR(p)-1) months:
MaxBen:=sum(p in PROJ, m in 1..NM-DUR(p)) (BEN(p)⁎(NM-m-DUR(p)+1)) ⁎ start(p,m)! Each project starts once and only once:

Fair Isaac Corporation Conﬁdential and Proprietary Information

35

Integer Programming
forall(p in PROJ) One(p):= sum(m in MONTHS) start(p,m) = 1! Resource availability:! A project starting in month m is in its k-m+1'st month in month k:forall(k in MONTHS) ResMax(k):=sum(p in PROJ, m in 1..k) RESUSE(p,k+1-m)⁎start(p,m) <= RESMAX(k)! Make all the start variables binary
forall(p in PROJ, m in MONTHS) start(p,m) is_binary
maximize(MaxBen)! Solve the MIP-problem

writeln("Solution value is: ", getobjval)
forall(p in PROJ) writeln( p, " starts in month ",getsol(sum(m in 1..NM-DUR(p)) m⁎start(p,m)) )end-model

Note that in the solution printout we apply the getsol function not to a single variable but to a linear
expression.

4.3

The project planning model using Special Ordered Sets
The example can be modiﬁed to use Special Ordered Sets of type 1 (SOS1). The startpm variables for agiven p form a set of variables which are ordered by m, the month. The ordering is induced by the
coefﬁcients of the startpm in the speciﬁcation of the SOS. For example, startp1 ’s coefﬁcient, 1, is lessthan startp2 ’s, 2, which in turn is less than startp3 ’s coefﬁcient, and so on The fact that the startpm
variables for a given p form a set of variables is speciﬁed to Mosel as follows:(! Define SOS-1 sets that ensure that at most one start(p,m) is non-zerofor each project p. Use month index to order the variables !)forall(p in PROJ) XSet(p):= sum(m in MONTHS) m⁎start(p,m) is_sos1

The is_sos1 speciﬁcation tells Mosel that Xset(p) is a Special Ordered Set of type 1.
The linear expression speciﬁes both the set members and the coefﬁcients that order the set members.
It says that all the startpm variables for m in the MONTHS index range are members of an SOS1 withreference row entries m.
The speciﬁcation of the startpm as binary variables must now be removed. The binary nature of thestartpm is implied by the SOS1 property, since if the startpm must add up to 1 and only one of them candiffer from zero, then just one is 1 and the others are 0.
If the two formulations are equivalent why were Special Ordered Sets invented, and why are they useful?
The answer lies in the way the reference row gives the search procedure in Integer Programming (IP)good clues as to where the best solution lies. Quite frequently the Linear Programming (LP) problem
that is solved as a ﬁrst approximation to an Integer Program gives an answer where startp1 is fractional,
say with a value of 0.5, and startp,NM takes on the same fractional value. The IP will say:‘my job is to get variables to 0 or 1. Most of the variables are already there so I will try moving xp1 or
xpT. Since the set members must add up to 1.0, one of them will go to 1, and one to 0. So I think that westart the project either in the ﬁrst month or in the last month.’
A much better guess is to see that the startpm are ordered and the LP solution is telling us it looks as if
the best month to start is somewhere midway between the ﬁrst and the last month. When sets arepresent, the IP can branch on sets of variables. It might well separate the months into those before themiddle of the period, and those later. It can then try forcing all the early startpm to 0, and restricting the
choice of the one startpm that can be 1 to the later startpm . It has this option because it now has theinformation to ‘know’ what is an early and what is a late startpm , whereas these variables wereunordered in the binary formulation.

Fair Isaac Corporation Conﬁdential and Proprietary Information

36

Integer Programming

The power of the set formulation can only really be judged by its effectiveness in solving large, difﬁcult
problems. When it is incorporated into a good IP system such as Xpress it is often found to be an orderof magnitude better than the equivalent binary formulation for large problems.

Fair Isaac Corporation Conﬁdential and Proprietary Information

37

CHAPTER 5

Overview of subroutines andreserved words
There is a range of built-in functions and procedures available in Mosel. They are described fully in the
Mosel Language Reference Manual. Here is a summary.


Accessing solution values: getsol, getact, getdual, getrcost, getslack, getobjval
Arithmetic functions: abs, arctan, cos, sin, ceil, floor, round, exp, ln, log, sqrt, isodd,
random, setrandseed
List functions: maxlist, minlist, cutelt, cutfirst, cutlast, cuthead, cuttail,findfirst, findlast, getelt, getfirst, getlast, getreverse, reverse, gethead,gettail, splithead, splittail

String functions: strfmt, substr, _Dynamic array handling: create, exists, finalize, delcell, isdynamicFile handling: fclose, fflush, fopen, fselect, fskipline, fwrite, fwrite_, fwriteln,
fwriteln_, getfid, getfname, getreadcnt, iseof, read, readln, write, write_,writeln, writeln_

Accessing control parameters: getparam, localsetparam, restoreparam, setparam

Getting information: getcoeff, getcoeffs, getsize, gettype, getvars

Constraint deﬁnition: sethidden, ishidden, makesos1, makesos2, setcoeff, setname,setrange, settypeTime and date: currentdate, currenttime, timestamp

Bit values: bitflip, bitneg, bitset, bitshift, bittest, bitvalSpecial values: isfinite, isinf, isnan

Miscellaneous functions: asproc, assert, compare, datablock, exit, exportprob, reset,
setioerr, setmatherr, publish, unpublish, memoryuse, newmuid, versionnum,
versionstr

Fair Isaac Corporation Conﬁdential and Proprietary Information

38

Overview of subroutines and reserved words

5.1

Modules
The distribution of Mosel contains several modules that add extra functionality to the language.
A full list of the functionality of a module can be obtained by using Mosel’s exam command, for instancemosel exam mmsystem

In this manual, we always use Xpress Optimizer as solver. Access to the corresponding optimization
functions is provided by the module mmxprs.
In the mmxprs module are the following useful functions.


Optimize: minimize, maximize

MIP directives: setmipdir, clearmipdir

Handling bases: savebasis, loadbasis, delbasis

Force problem loading: loadprob

Accessing problem status: getprobstat

Deal with bounds: setlb, setub, getlb, getubModel cut functions: setmodcut, clearmodcut

For example, here is a nice habit to get into when solving a problem with Xpress Optimizer.
declarations
status:array({XPRS_OPT,XPRS_UNF,XPRS_INF,XPRS_UNB,XPRS_OTH}) of string
end-declarations
status::([XPRS_OPT,XPRS_UNF,XPRS_INF,XPRS_UNB,XPRS_OTH])["Optimum found","Unfinished","Infeasible","Unbounded","Failed"]...
minimize(Obj)writeln(status(getprobstat))

In the mmsystem module are various useful functions provided by the underlying operating system anda few programming utilities :Delete a ﬁle/directory: fdelete, removedir

Copy/move a ﬁle: fcopy, fmoveMake a directory: makedirCurrent working directory: getcwd

Get/set an environment variable’s value: getenv, setenv

File and system status: getfstat, getsysstat

General system call: system

Time and date: gettime, getdate, getweekday, getasnumber, ...



Handling the type text: copytext, cuttext, deltext, readtextline, ...



Sort an array of any type with ’order’ property: qsort

Other modules mentioned in this manual are mmodbc, mmsheet, mmetc, and mmjobs.
See the module documentation in the Mosel Language Reference Manual or in the individual module
reference manuals for full details.

Fair Isaac Corporation Conﬁdential and Proprietary Information

39

Overview of subroutines and reserved words

5.2

Reserved words
The following words are reserved in Mosel. The upper case versions are also reserved (i.e. AND andand are keywords but not And). Do not use them in a model except with their built-in meaning.
and, array, as
boolean, breakcase, constant, count, counterdeclarations, div, do, dynamic
elif, else, end, evaluation
false, forall, forward, from, function
hashmap
if, imports, in, include, initialisations, initializations, integer, inter,
is_binary, is_continuous, is_free, is_integer, is_partint, is_semcont,is_semint, is_sos1, is_sos2
linctr, list
max, min, mod, model, mpvar
namespace, next, not, nsgroup, nssearchof, options, or
package, parameters, procedure, public, prod
range, real, record, repeat, requirements, return
set, shared, string, sumthen, to, true
union, until, uses
versionwhile, with

Fair Isaac Corporation Conﬁdential and Proprietary Information40

CHAPTER 6

Correcting errors in Mosel models
The parser of Mosel is able to detect a large number of errors that may occur when writing a model. In
this chapter we shall try to analyze and correct some of these. As a next step, we also show how to
obtain information for dealing with run time errors.
Other types of errors that are in general more difﬁcult to detect are mistakes in the data or logical errors
in the formulation of Mosel models—you may use the Mosel Debugger (see Section 15.1) to trace these.

6.1

Correcting syntax errors in Mosel
If we compile the model poerror1.mosmodel `Plenty of errors'
declarations
small, large: mpvar
end-declarations
Profit=5⁎small + 20⁎large
Boxwood:=small + 3⁎large <= 200
Lathe:=
3⁎small + 2⁎large <= 160
maximize(Profit)
writeln("Best profit is ", getobjval
end-model

we get the following output:
Mosel: E-100 at (1,7) of `poerror.mos': Syntax error before ``'.
Parsing failed.

The second line of the output informs us that the compilation has not been executed correctly. The ﬁrst
line tells us exactly the type of the error that has been detected, namely a syntax error with the code
E-100 (where E stands for error) and its location: line 1 character 7. The problem is caused by theapostrophe ‘ (or something preceding it). Indeed, Mosel expects either single or double quotes aroundthe name of the model if the name contains blanks. We therefore replace it by ’ and compile thecorrected model, resulting in the following display:
Mosel: E-100 at (6,8) of `poerror.mos': Syntax error before `='.
Mosel: W-121 at (6,29) of `poerror.mos': Statement with no effect.
Mosel: E-100 at (10,16) of `poerror.mos': `Profit' is not defined.
Mosel: E-123 at (10,17) of `poerror.mos': `maximize' is not defined.
Mosel: E-100 at (12,37) of `poerror.mos': Syntax error.
Parsing failed.

Fair Isaac Corporation Conﬁdential and Proprietary Information

41

Correcting errors in Mosel models

There is a problem with the sign = in the 6th line:
Profit=5⁎small + 20⁎large

In the model body the equality sign = may only be used in the deﬁnition of constraints or in logicalexpressions. Constraints are linear relations between variables, but profit has not been deﬁned as avariable, so the parser detects an error. What we really want, is to assign the linear expression
5⁎small + 20⁎large to Profit. For such an assignment we have to use the sign :=. Using just =is a very common error.
As a consequence of this error, the linear expression after the equality sign does not have any relevanceto the problem that is stated. The parser informs us about this fact in the second line: it has found astatement with no effect. This is not an error that would cause the failure of the compilation taken on
its own, but simply a warning (marked by the W in the error code W-121) that there may be something tolook into. Since Profit has not been deﬁned, it cannot be used in the call to the optimization, hence
the third error message.
As we have seen, the second and the third error messages are consequences of the ﬁrst mistake wehave made. Before looking at the last message that has been displayed we recompile the model withthe corrected line
Profit:=5⁎small + 20⁎large

to get rid of all side effects of this error. Unfortunately, we still get a few error messages:
Mosel: E-123 at (10,17) of `poerror.mos': `maximize' is not defined.
Mosel: E-100 at (12,37) of `poerror.mos': Syntax error.

There is still a problem in line 10; this time it shows up at the very end of the line. Although everythingappears to be correct, the parser does not seem to know what to do with maximize. The solution tothis enigma is that we have forgotten to load the module mmxprs that provides the optimization
function maximize. To tell Mosel that this module is used we need to add the line
uses "mmxprs"

immediately after the start of the model, before the declarations block. Forgetting to specify mmxprs is
another common error. We now have a closer look at line 12 (which has now become line 13 due to theaddition of the uses statement). All subroutines called in this line (writeln and getobjval) areprovided by Mosel, so there must be yet another problem: we have forgotten to close the parentheses.
After adding the closing parenthesis after getobjval the model ﬁnally compiles without displaying
any errors. If we run it we obtain the desired output:
Best profit is 1333.33
Returned value: 0

6.2

Correcting run time errors in Mosel
Besides the detection of syntax errors, Mosel may also give some help in ﬁnding run time errors. It
should only be pointed out here that it is possible to add the ﬂag -g to the compile command to obtainsome information about where the error occurred in the program, resulting in a command sequencesuch asmosel compile -g mymodel.mos
mosel mymodel.bim

or short

Fair Isaac Corporation Conﬁdential and Proprietary Information

42

Correcting errors in Mosel models
mosel exec -g mymodel

Also useful is turning on verbose reporting, for instancesetparam("XPRS_VERBOSE",true)
setparam("XPRS_LOADNAMES",true)

Fair Isaac Corporation Conﬁdential and Proprietary Information

43

II. Advanced language features

Overview
This part takes the reader who wants to use Mosel as a modeling, solving and programming
environment through its powerful programming language facilities. The following topics, most of whichhave already shortly been mentioned in the ﬁrst part, are covered in a more detailed way:

Selections and loops (Chapter 7)

Working with arrays, sets, lists, and records (Chapter 8)Functions and procedures (Chapter 9)Output to ﬁles and producing formatted output (Chapter 10)

Whilst the ﬁrst four chapters in this part present pure programming examples, the last two chapters
contain some advanced examples of LP and MIP that make use of the programming facilities in Mosel:

Cut generation (Section 11.1)

Column generation (Section 11.2)



Recursion or Successive Linear Pogramming (Section 12.1)

Goal Programming (Section 12.2)

Fair Isaac Corporation Conﬁdential and Proprietary Information

45

CHAPTER 7

Flow control constructs
Flow control constructs are mechanisms for controlling the order of the execution of the actions in a
program. In this chapter we are going to have a closer look at two fundamental types of control
constructs in Mosel: selections and loops.
Frequently actions in a program need to be repeated a certain number of times, for instance for all
possible values of some index or depending on whether a condition is fulﬁlled or not. This is the
purpose of loops. Since in practical applications loops are often interwoven with conditions (selection
statements), these are introduced ﬁrst.

7.1

Selections
Mosel provides several statements to express a selection between different actions to be taken in a
program. The simplest form of a selection is the if-then statement:

if-then: ‘If a condition holds do something’. For example:
if A >= 20 thenx <= 7
end-if

For an integer number A and a variable x of type mpvar, x is constrained to be less or equal to 7 if A is
greater or equal 20.
Note that there may be any number of expressions between then and end-if, not just a single one.
In other cases, it may be necessary to express choices with alternatives.


if-then-else: ‘If a condition holds, do this, otherwise do something else’. For example:
if A >= 20 thenx <= 7else x >= 35
end-ifHere the upper bound 7 is applied to the variable x if the value of A is greater or equal 20,
otherwise the lower bound 35 is applied to it.


if-then-elif-then-else: ‘If a condition holds do this, otherwise, if a second condition holds dosomething else etc.’
if A >= 20 thenx <= 7
elif A <= 10 thenx >= 35

Fair Isaac Corporation Conﬁdential and Proprietary Information46

Flow control constructs
else
x = 0
end-ifHere the upper bound 7 is applied to the variable x if the value of A is greater or equal 20, and if
the value of A is less or equal 10 then the lower bound 35 is applied to x. In all other cases (that is,
A is greater than 10 and smaller than 20), x is ﬁxed to 0.
Note that this could also be written using two separate if-then statements but it is more
efﬁcient to use if-then-elif-then[-else] if the cases that are tested are mutually
exclusive.
case: ‘Depending on the value of an expression do something’.
case A of-MAX_INT..10 : x >= 35
20..MAX_INT : x <= 7
12, 15 :
x = 1
else
x = 0
end-case

Here the upper bound 7 is applied to the variable x if the value of A is greater or equal 20, and thelower bound 35 is applied if the value of A is less or equal 10. In addition, x is ﬁxed to 1 if A has
value 12 or 15, and ﬁxed to 0 for all remaining values.
An example for the use of the case statement is given in Section 12.2.
The following example (model minmax.mos) uses the if-then-elif-then statement to computethe minimum and the maximum of a set of randomly generated numbers:
model Minmax
declarations
SNumbers: set of integer
LB=-1000
UB=1000
end-declarations! Elements of SNumbers must be between LB! and UB

! Generate a set of 50 randomly chosen numbers
forall(i in 1..50)
SNumbers += {round(random⁎200)-100}
writeln("Set: ", SNumbers, " (size: ", getsize(SNumbers), ")")
minval:=UB
maxval:=LB
forall(p in SNumbers)
if p<minval thenminval:=pelif p>maxval thenmaxval:=p
end-ifwriteln("Min: ", minval, ", Max: ", maxval)
end-model

Instead of writing the loop above, it would of course be possible to use the corresponding operators
min and max provided by Mosel:
writeln("Min: ", min(p in SNumbers) p, ", Max: ", max(p in SNumbers) p)

It is good programming practice to indent the block of statements in loops or selections as in thepreceding example so that it becomes easy to get an overview where the loop or the selection ends. —At the same time this may serve as a control whether the loop or selection has been terminatedcorrectly (i.e. no end-if or similar key words terminating loops have been left out).

Fair Isaac Corporation Conﬁdential and Proprietary Information47

Flow control constructs

7.2

Loops
Loops group actions that need to be repeated a certain number of times, either for all values of someindex or counter (forall) or depending on whether a condition is fulﬁlled or not (while,
repeat-until).
This section presents the complete set of loops available in Mosel, namely forall, forall-do,
while, while-do, and repeat-until.

7.2.1 forall
The forall loop repeats a statement or block of statements for all values of an index or counter. If theset of values is given as an interval of integers (range), the enumeration starts with the smallest value.
For any other type of sets the order of enumeration depends on the current (internal) order of theelements in the set.
The forall loop exists in two different versions in Mosel. The inline version of the forall loop (i.e.
looping over a single statement) has already been used repeatedly, for example as in the following loopthat constrains variables x(i) (i=1,...,10) to be binary.
forall(i in 1..10) x(i) is_binary

The second version of this loop, forall-do, may enclose a block of statements, the end of which ismarked by end-do.
Note that the indices of a forall loop can not be modiﬁed inside the loop. Furthermore, they must be
new objects: a symbol that has been declared cannot be used as index of a forall loop.
The following example (model perfect.mos) that calculates all perfect numbers between 1 and agiven upper limit combines both types of the forall loop. (A number is called perfect if the sum of its
divisors is equal to the number itself.)model Perfectparameters
LIMIT=100
end-parameters
writeln("Perfect numbers between 1 and ", LIMIT, ":")
forall(p in 1..LIMIT) do
sumd:=1
forall(d in 2..p-1)
if p mod d = 0 thensumd+=dend-ifif p=sumd thenwriteln(p)end-ifend-do! Mosel's built-in mod operator! The same as sum:= sum + d

end-model

The outer loop encloses several statements, we therefore need to use forall-do. The inner loop only
applies to a single statement (if statement) so that we may use the inline version forall.
If run with the default parameter settings, this program computes the solution 1, 6, 28.

Fair Isaac Corporation Conﬁdential and Proprietary Information48

Flow control constructs

7.2.1.1

Multiple indices

The forall statement (just like the sum operator and any other statement in Mosel that requires index
set(s)) may take any number of indices, with values in sets of any basic type or ranges of integer
values. If two or more indices have the same set of values as inforall(i in 1..10, j in 1..10) y(i,j) is_binary(where y(i,j) are variables of type mpvar) the following equivalent short form may be used:
forall(i,j in 1..10) y(i,j) is_binary

7.2.1.2

Conditional looping

The possibility of adding conditions to a forall loop via the ‘|’ symbol has already been mentioned in
Chapter 3. Conditions may be applied to one or several indices and the selection statement(s) can beplaced accordingly. Take a look at the following example where A and U are one- and two-dimensional
arrays of integers or reals respectively, and y a two-dimensional array of decision variables (mpvar):
forall(i in -10..10, j in 0..5 | A(i) > 20) y(i,j) <= U(i,j)

For all i from -10 to 10, the upper bound U(i,j) is applied to the variable y(i,j) if the value of A(i)is greater than 20.
The same conditional loop may be reformulated (in an equivalent but usually less efﬁcient way) using
the if statement:
forall(i in -10..10, j in 0..5)
if A(i) > 20y(i,j) <= U(i,j)
end-ifIf we have a second selection statement on both indices with B a two-dimensional array of integers orreals, we may either write
forall(i in -10..10, j in 0..5 | A(i) > 20 and B(i,j) <> 0 ) y(i,j) <= U(i,j)

or, more efﬁciently, since the second condition on both indices is only tested if the condition on index iholds:
forall(i in -10..10 | A(i) > 20, j in 0..5 | B(i,j) <> 0 ) y(i,j) <= U(i,j)

7.2.1.3

Counters

A recurring programming task when working with loops, and in particular with conditional loops, is todetermine the number of times that the loop has been executed (or that the loop condition is veryﬁed).
To this aim, Mosel provides the construct as counter, to be added to the indices of forall loops orother statements involving indices, such as sum, max, or union statements.
The following example (see ﬁle count1.mos) counts and displays all strings in a list that contain thesubstring ’b’:
L:= ['a', 'ab', 'abc', 'da', 'bc', 'db']
scnt:=0
forall(scnt as counter, s in L | findtext(s, 'b', 1)>0)
writeln(scnt, ": ", s)

Fair Isaac Corporation Conﬁdential and Proprietary Information49

Flow control constructs

The position of as counter among the loop indices is entirely up to the programmer and takes noeffect on its value. However notice that loop conditions must succeed an index. So for instance,
instead of the above we might equally have written:
forall(s in L | findtext(s, 'b', 1)>0, scnt as counter)
writeln(scnt, ": ", s)And here is an elegant formulation how to calculate the average value of set elements with a given
property (odd numbers):
S:= {1, 5, 8, -1, 4, 7, 2}
cnt:=0.0
writeln("Average of odd numbers: ",(sum(cnt as counter, i in S | isodd(i)) i) / cnt)

As an alternative to adding a counter on a loop, Mosel also deﬁnes the aggregate operator count thatis used as follows.
writeln("Number of odd numbers in S: ", count(i in S | isodd(i)) )writeln("Occurences of 'b' in L: ", count(s in L | findtext(s, 'b', 1)>0) )Both types of counters may be used jointly in a single statement as shown in the following example(model count2.mos) that creates an entry for the array NODE if there are at least two incoming oroutgoing arcs for the corresponding index j.
declarations
I: set of integer
ARC: dynamic array(I,I) of boolean
NODE: dynamic array(set of integer) of integer
end-declarations
initializations from "count2.dat"
ARC
end-initializations
ctnode:=0
forall(ctnode as counter, j in I |count(i in I | exists(ARC(i,j))) +count(i in I | exists(ARC(j,i))) >= 2) create(NODE(j))
writeln("Number of nodes created: ", ctnode)

7.2.2 while
A while loop is typically employed if the number of times that the loop needs to be executed is notknow beforehand but depends on the evaluation of some condition: a set of statements is repeated
while a condition holds. As with forall, the while statement exists in two versions, an inline version(while) and a version (while-do) that is to be used with a block of program statements.
The following example (model lcdiv1.mos) computes the largest common divisor of two integer
numbers A and B (that is, the largest number by which both A and B, can be divided without remainder).
Since there is only a single if-then-else statement in the while loop we could use the inline
version of the loop but, for clarity’s sake, we have given preference to the while-do version that marks
where the loop terminates clearly.
model Lcdiv1
declarations
A,B: integer

Fair Isaac Corporation Conﬁdential and Proprietary Information50

Flow control constructs
end-declarations
write("Enter two integer numbers:\nreadln(A)write(" B: ")readln(B)A: ")while (A <> B) do
if (A>B) then
A:=A-Belse B:=B-A
end-ifend-dowriteln("Largest common divisor: ", A)
end-model

7.2.3 repeat until
The repeat-until structure is similar to the while statement with the difference that the actions in
the loop are executed once before the termination condition is tested for the ﬁrst time.
The following example (model shsort.mos) combines the three types of loops (forall, while,
repeat-until) that are available in Mosel. It implements a Shell sort algorithm for sorting an array ofnumbers into numerical order. The idea of this algorithm is to ﬁrst sort, by straight insertion, small
groups of numbers. Then several small groups are combined and sorted. This step is repeated until thewhole list of numbers is sorted.
The spacings between the numbers of groups sorted on each pass through the data are called theincrements. A good choice is the sequence which can be generated by the recurrenceinc1 = 1, inck+1 = 3 · inck + 1, k = 1, 2, ...
model "Shell sort"declarations
N: integer
ANum: array(range) of real
end-declarations! Size of array ANum
! Unsorted array of numbers

N:=50
forall(i in 1..N)
ANum(i):=round(random⁎100)
writeln("Given list of numbers (size: ", N, "): ")forall(i in 1..N) write(ANum(i), " ")writelninc:=1
repeatinc:=3⁎inc+1
until (inc>N)! Determine the starting increment

repeat! Loop over the partial sorts
inc:=inc div 3
forall(i in inc+1..N) do! Outer loop of straight insertion
v:=ANum(i)j:=iwhile (ANum(j-inc)>v) do! Inner loop of straight insertion
ANum(j):=ANum(j-inc)
j -= incif j<=inc then break; end-if
end-doANum(j):= v
end-dountil (inc<=1)

Fair Isaac Corporation Conﬁdential and Proprietary Information

51

Flow control constructs
writeln("Ordered list: ")forall(i in 1..N) write(ANum(i), " ")writelnend-model

The example introduces a new statement: break. It can be used to interrupt one or several loops. In
our case it stops the inner while loop. Since we are jumping out of a single loop, we could as well
write break 1. If we wrote break 3, the break would make the algorithm jump 3 loop levels higher,that is outside of the repeat-until loop.
Note that there is no limit to the number of nested levels of loops and/or selections in Mosel.

Fair Isaac Corporation Conﬁdential and Proprietary Information

52

CHAPTER 8

Arrays, sets, lists, and records
The Mosel language deﬁnes the structured types set, array, list, and record. So far we have worked witharrays and sets relying on an intuitive understanding of what is an ‘array’ or a ‘set’. More formally, we
may deﬁne an array as a collection of labeled objects of a given type where the label of an array entry is
deﬁned by its index tuple.
A set collects objects of the same type without establishing an order among them (as opposed toarrays and lists). Set elements are unique: if the same element is added twice the set still only contains
it once.
A list groups objects of the same type. Unlike sets, a list may contain the same element several times.
The order of the list elements is speciﬁed by construction.
Mosel arrays, sets and lists may be deﬁned for any type, that is the elementary types (including thebasic types integer, real, string, boolean and the MP types mpvar and linctr), structured
types (array, set, list, record), and external types (contributed to the language by a module).
A record is a ﬁnite collection of objects of any type. Each component of a record is called a ﬁeld and ischaracterized by its name and its type.
This chapter ﬁrst presents in a more systematic way the different possibilities of how arrays and sets
may be initialized (all of which the reader has already encountered in the examples in the ﬁrst part), andalso shows more advanced ways of working with sets. We then introduce lists, showing how toinitialize and access them, and ﬁnally give some examples of the use of records.

8.1

ArraysIn the ﬁrst part of this manual we have already encountered many examples that make use of arrays.
The most important points are summarized in this section and here is an overview of the topics
explained with other examples:

The initialization operator :: and value assignment: Section 2.1

Multidimensional arrays: Section 2.1

String indices: Section 2.1.3



Initialization from ﬁle:– dense format text ﬁle (Section 2.2),– ODBC connection (Section 2.2.5),– Excel spreadsheets (Section 2.2.5.1),– sparse format text ﬁle (Section 3.2),– alternative text formats (Section 3.4)
Dynamic variable creation and ﬁnalization: Section 3.3.1

Fair Isaac Corporation Conﬁdential and Proprietary Information

53

Arrays, sets, lists, and records

8.1.1

Array declaration
Here are some examples of array deﬁnition:
declarations
A1: array(1..3) of integer
F = {"a","b","c"}A2: array(F) of real
A3: array(R:range) of integer
A4: dynamic array(F) of real
end-declarations! Fixed size array! Fixed size array! Dense array with unknown index set! Dynamic arraywriteln("A1:", A1, " A2:", A2, " A3:", A3, " A4:", A4, " A5:", A5)! Using the array initialization operator
A1::[10,20,30]! Range indices are known
A2::(["a","b","c"])[1.1, 2.5, 3.9] ! String indices must be stated
A3::(1..3)[10,20,30]! Indices are not known upfront
A2("a"):=5.1! Redefine an entry

setrandseed(3)forall(f in F) A4(f):= 10⁎random
delcell(A4("a"))! Value assignment! Deleting an array entry

writeln("A1:", A1, " A2:", A2, " A3:", A3, " A4:", A4)

The output produced by this model (ﬁle arraydef.mos) is the following.
A1:[0,0,0] A2:[0,0,0] A3:[] A4:[]
A1:[10,20,30] A2:[5.1,2.5,3.9] A3:[(1,10),(2,20),(3,30)] A4:[(`b',7.6693),(`c',5.89101)]

Arrays A1 and A2 are ﬁxed size arrays: their size (i.e. the total number of objects/cells they contain) is
known at their declaration because all their indexing sets are of ﬁxed size (i.e. either constant or
ﬁnalized). All the cells of ﬁxed size arrays are created and initialized immediately, using default
initialization values that depend on the array type. For Mosel’s basic types these are the following
values.
real, integer: 0
boolean: false
string: ’’ (i.e. the empty string)
Array A4 is explicitly marked as dynamic array using the qualiﬁer dynamic. Dynamic arrays along withhashmap arrays are the two forms of sparse arrays in Mosel, a hashmap array is obtained by applying
the qualiﬁer hashmap in place of dynamic—both types are used in the same way, but theirperformance differs (dynamic arrays are generally faster for linear enumeration and require lessmemory whereas hashmap arrays are faster for random access). Sparse arrays are created empty.
Their cells are created explicitly (see Paragraph 8.1.1.2 below) or when they are assigned a value, thatis, the array size will grow ‘on demand’. It is also possible to delete some or all cells of a sparse arrayusing the procedure delcell on an entry or the whole array (same as reset). The value of a cell thathas not been created is the default initial value of the type of the array.
Array A3 is created empty since its indexing set is empty at the time of its declaration, but this array is
not the same as a dynamic array. It is a dense array that will grow if elements are added to its index set.
Please refer to Appendix B.3 for further detail.
8.1.1.1

Multiple indices

Arrays with multiple indices are deﬁned and accessed as follows:declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information

54

Arrays, sets, lists, and records
C: array(range, set of string, set of real) of integer
D: array(1..5) of array(1..10) of real
end-declarations
C(5,"a",1.5):= 10
D(1,7):= 2.8

As shown in the example, in order to access (or ’dereference’) the cell of an array of arrays, the list of
indices for the second array has to be appended to the list of indices of the ﬁrst array.
The declaration of the arrays in the code snippet above shows several different types of index sets: themost common index set types probably are range sets, and sets of types integer or string. Mosel
accepts any type of set as array index (including sets of structured types), however, for most pratical
purposes it is recommended to employ only constant types as array indices (that is, the four basic
types integer, string, boolean, real, or external types such as date/time/datetime that
support the ’constant’ property: see the example in Section 17.5.2). Note that while it is possible to use
index sets of type real for Mosel arrays this is not a generally encouraged practice: due to theunderlying ﬂoating point representation it is not always guaranteed that two index values that look thesame are indeed identical.
8.1.1.2 create
Special care needs to be taken in the case of sparse arrays of decision variables (and indeed with any
types that do not have an assignment operator). Writing x:=1 is a syntax error if x is of type mpvar. If
an array of such a type is deﬁned as dynamic or hashmap array, then the corresponding cells are notcreated. The entries of the array must be created explicitly by using the procedure create since theycannot be deﬁned by assignment. Let us simply recall here the example from Section 3.2.
declarations
REGION: set of string! Set of customer regions
PLANT: set of string! Set of plants
TRANSCAP: dynamic array(PLANT,REGION) of real
! Capacity on each route plant->region
flow: dynamic array(PLANT,REGION) of mpvar! Flow on each routeend-declarations
initializations from 'transprt.dat'
TRANSCAP
end-initializations! Create the flow variables that exist
forall(p in PLANT, r in REGION | exists(TRANSCAP(p,r)) ) create(flow(p,r))

For a more detailed discussion of decision variable creation please see Section 3.3.1.

8.1.2

Array initialization from ﬁleWhen working with arrays, we distinguish between dense and sparse data formats. Dense data format
means that only the data values are represented (see also Section 2.2); in sparse format each data
entry is accompanied by its index tuple. Dense format data uses less storage space but this format can
only be used if all indices are deﬁned in the model and if no ambiguity results from the omission of theindices when transferring data. In all other cases sparse data format must be used and it is particularly
recommended to use this representation if only few entries of a multidimensional array are actuallydeﬁned.
declarations
A: array(1..2,1..3) of real
! Can use dense format
B: array(R:range,T:range) of real
! Requires sparse format
D: dynamic array(set of string, range) of real
! Requires sparse format

Fair Isaac Corporation Conﬁdential and Proprietary Information

55

Arrays, sets, lists, and records
S: set of string
M: dynamic array(S) of integer
N: dynamic array(S) of string
end-declarations
initializations from "arrayinit.dat"
A B
D as "SomeName"
D as "SomeName2"[M,N] as "MNData"
end-initializations! Requires sparse format! Requires sparse format! Data label different from model name
! Add some more data to 'D'! 2 arrays read from the same table

writeln("A:", A, " B:", B, "\nD:", D, "\nM:", M, "\nN:", N)

With this contents of the data ﬁle arrayinit.dat
A: [2 4 6 8 10 12]
B: [(1 1) 2 (1 2) 4 (1 3) 6 (2 1) 8 (2 2) 10 (2 3) 12]
SomeName: [("a" 1) 2 ("a" 2) 4 ("b" 3) 6 ("c" 4) 8 ("b" 5) 10]
SomeName2: [("a" 3) 12 ("b" 2) 14 ("b" 5) 16]
MNData: [ ("A") [2 "a"] ("B") [⁎ "b"]("C") [6 ⁎]("D") [8 "c"]("E") [10 "b"] ]we see the following output display when executing the model arrayinit.mos shown above:
A:[2,4,6,8,10,12] B:[2,4,6,8,10,12]D:[(`a',1,2),(`a',2,4),(`a',3,12),(`b',2,14),(`b',3,6),(`b',5,16),(`c',4,8)]
M:[(`A',2),(`C',6),(`D',8),(`E',10)]N:[(`A',a),(`B',b),(`D',c),(`E',b)]

By default, Mosel expects that data labels are the same as the model names. For array D we show how
to read data using different labels. The contents of the second set of data labeled SomeName2 is added
to what is read from SomeName. Note that the entry (b,5) is contained in both sets, and thecorresponding array entry takes its value from the last label that is read.
Arrays such as M and N, that share the same index sets (but not necessarily the same entries) can beread from a single label/data table. The ‘⁎’ in certain entries of MNData indicates that the entry does
not exist in one of the arrays.
The syntax of initializations blocks remains the same when switching to other data sources.
Sections 2.2.5 and 2.2.5.1 discuss examples of using databases or spreadsheets instead of text ﬁlesfor array initialization. For further detail on data I/O using different data sources the reader is refered to
the Xpress whitepaper Using ODBC and other database interfaces with Mosel.

8.1.3

Automatic arrays: the array operator
The keyword array can be used as an aggregate operator in order to create an array that will exist only
for the duration of the expression. This automatic array may be used wherever a reference to an array is
expected, for instance, in function calls or in initializations blocks.
In the following example we use the array operator to extract the (1-dimensional) rows and column
arrays from a 2-dimensional array, we further generate a subarray with a selection of entries and thetransposed (inversed indices) array.
model "Automatic arrays"
declarations
B: array(S:set of string, I:set of real) of integer
end-declarations
B::(["a","b"], [3,1.5,7])[1,2,3,4,5,6]

Fair Isaac Corporation Conﬁdential and Proprietary Information

56

Arrays, sets, lists, and records
writeln("B: ", B)
forall(s in S) writeln("Row ", s, ": ", array(i in I) B(s,i))
forall(i in I) writeln("Column ", i, ": ", array(s in S) B(s,i))writeln("B filtered: ", array(s in S,i in I | s<>"a" and i<5) B(s,i))writeln("Transpose: ", array(i in I, s in S) B(s,i))
end-model

And this is the output generated by the model autoarray.mos.
B: [1,2,3,4,5,6]
Row a: [1,2,3]
Row b: [4,5,6]
Column 3: [1,4]Column 1.5: [2,5]Column 7: [3,6]B filtered: [(`b',3,4),(`b',1.5,5)]Transpose: [1,4,2,5,3,6]

As it has been mentioned in Section 8.1.1.1 the use index sets of type real for Mosel arrays is not a
generally encouraged practice: due to the underlying ﬂoating point representation it is not always
guaranteed that two index values that look the same are indeed identical.
On the topic of output to ﬁle using initializations to, see Chapter 10, and particularly the note on
solution output using arrays generated ’on the ﬂy’ in combination with evaluation of in Section
10.2.4.

8.2

Initializing sets
In the revised formulation of the burglar problem in Chapter 2 and also in the models in Chapter 3 wehave already seen different examples for the use of index sets. We recall here the relevant parts of therespective models.

8.2.1

Constant sets
In the Burglar example the index set is assigned directly in the model:
declarations
ITEMS={"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}
end-declarations

Since in this example the set contents is set in the declarations section, the index set ITEMS is aconstant set (its contents cannot be changed). To declare it as a dynamic set, the contents needs to be
assigned after its declaration:
declarations
ITEMS: set of string
end-declarations
ITEMS:={"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}

8.2.2

Set initialization from ﬁle, ﬁnalized and ﬁxed sets
In Chapter 4 the reader has encountered several examples how the contents of sets may be initialized
from data ﬁles.

Fair Isaac Corporation Conﬁdential and Proprietary Information

57

Arrays, sets, lists, and records

The contents of the set may be read in directly as in the following case:declarations
WHICH: set of integer
end-declarations
initializations from 'idata.dat'
WHICH
end-initializations

Where idata.dat contains data in the following format:
WHICH: [1 4 7 11 14]

Unless a set is constant (or ﬁnalized), arrays that are indexed by this set (and that are not explicitly
marked as sparse arrays) are created as non-ﬁxed dense arrays. Since in many cases the contents of aset does not change any more after its initialization, Mosel’s automatic ﬁnalization mechanism ﬁnalizes
the set WHICH in the initializations from block. Consider the continuation of the example
above:
declarations
x: array(WHICH) of mpvar
end-declarations

The array of variables x will be created as a static array since its index set is ﬁnalized. Declaring arraysin the form of static arrays is preferable if the indexing set is known before because this allows Mosel
to handle them in a more efﬁcient way.
Index sets may also be initialized indirectly during the initialization of non-ﬁxed or sparse arrays:
declarations
REGION: set of string
DEMAND: array(REGION) of real
end-declarations
initializations from 'transprt.dat'
DEMAND
end-initilizations

If ﬁle transprt.dat contains the data:
DEMAND: [(Scotland) 2840 (North) 2800 (West) 2600 (SEast) 2820 (Midlands) 2750]

then printing the set REGION after the initialization will give the following output:{`Scotland',`North',`West',`SEast',`Midlands'}

Once a set is used for indexing an array (of data, decision variables etc.) it is ﬁxed, that is, its elements
can no longer be removed, but it may still grow in size.
The indirect initialization of (index) sets is not restricted to the case that data is input from ﬁle. In thefollowing example (model chess2.mos) we add an array of variable descriptions to the chess problem
introduced in Chapter 1. These descriptions may, for instance, be used for generating a nice output.
Since the indexing set Allvars of array DescrV is not known at declaration time the resulting array is
not ﬁxed and both grow with each new variable description that is added to DescrV.
model "Chess 2"
uses "mmxprs"
declarations
Allvars: set of mpvar
DescrV: array(Allvars) of string

Fair Isaac Corporation Conﬁdential and Proprietary Information

58

Arrays, sets, lists, and records
small, large: mpvar
end-declarations
DescrV(small):= "Number of small chess sets"
DescrV(large):= "Number of large chess sets"
Profit:= 5⁎small + 20⁎large
Lathe:=
3⁎small + 2⁎large <= 160
Boxwood:=small + 3⁎large <= 200
maximize(Profit)
writeln("Solution:\n Objective: ", getobjval)
writeln(DescrV(small), ": ", getsol(small))writeln(DescrV(large), ": ", getsol(large))
end-model

The reader may have already remarked another feature that is illustrated by this example: the indexing
set Allvars is of type mpvar. So far only basic types have occurred as index set types but as
mentioned earlier, sets in Mosel may be of any elementary type, including the MP types mpvar andlinctr.

8.3

Working with sets
In all examples of sets given so far sets are used for indexing other modeling objects. But they may
also be used for different purposes.
The following example (model setops.mos) demonstrates the use of basic set operations in Mosel:
union (+), intersection (⁎), and difference (-):
model "Set example"
declarations
Cities={"rome", "bristol", "london", "paris", "liverpool"}Ports={"plymouth", "bristol", "glasgow", "london", "calais","liverpool"}
Capitals={"rome", "london", "paris", "madrid", "berlin"}
end-declarations
Places:= Cities+Ports+Capitals
In_all_three:= Cities⁎Ports⁎Capitals
Cities_not_cap:= Cities-Capitals! Create the union of all 3 sets
! Create the intersection of all 3 sets
! Create the set of all cities that are! not capitals

writeln("Union of all places: ", Places)
writeln("Intersection of all three: ", In_all_three)writeln("Cities that are not capitals: ", Cities_not_cap)
end-model

The output of this example will look as follows:
Union of all places:{`rome',`bristol',`london',`paris',`liverpool',`plymouth',`bristol',`glasgow',`calais',`liverpool',`rome',`paris',`madrid',`berlin'}
Intersection of all three: {`london'}Cities that are not capitals: {`bristol',`liverpool}

Sets in Mosel are indeed a powerful facility for programming as in the following example (model
prime.mos) that calculates all prime numbers between 2 and some given limit.
Starting with the smallest one, the algorithm takes every element of a set of numbers SNumbers

Fair Isaac Corporation Conﬁdential and Proprietary Information

59

Arrays, sets, lists, and records(positive numbers between 2 and some upper limit that may be speciﬁed when running the model),adds it to the set of prime numbers SPrime and removes the number and all its multiples from the set
SNumbers.
model Prime
parameters
LIMIT=100
end-parameters! Search for prime numbers in 2..LIMIT

declarations
SNumbers: set of integer
SPrime: set of integer
end-declarations! Set of numbers to be checked! Set of prime numbers

SNumbers:={2..LIMIT}
writeln("Prime numbers between 2 and ", LIMIT, ":")
n:=2
repeatwhile (not(n in SNumbers)) n+=1
SPrime += {n}! n is a prime number
i:=nwhile (i<=LIMIT) do! Remove n and all its multiples
SNumbers-= {i}i+=n
end-dountil SNumbers={}
writeln(SPrime)
writeln(" (", getsize(SPrime), " prime numbers.)")end-modelThis example uses a new function, getsize, that if applied to a set returns the number of elements ofthe set. The condition in the while loop is the logical negation of an expression, marked with not: theloop is repeated as long as the condition n in SNumbers is not satisﬁed.

8.3.1

Set operators
The preceding example introduces the operator += to add sets to a set (there is also an operator -= toremove subsets from a set). Another set operator used in the example is in denoting that a single
object is contained in a set. We have already encountered this operator in the enumeration of indicesfor the forall loop.
Mosel also deﬁnes the standard operators for comparing sets: subset (<=), superset (>=), difference(<>), end equality (=). Their use is illustrated by the following example (model setcomp.mos):model "Set comparisons"declarations
RAINBOW = {"red", "orange", "yellow", "green", "blue", "purple"}
BRIGHT = {"yellow", "orange"}
DARK = {"blue", "brown", "black"}
end-declarations
writeln("BRIGHT is included in RAINBOW: ", BRIGHT <= RAINBOW)
writeln("RAINBOW is a superset of DARK: ", RAINBOW >= DARK)
writeln("BRIGHT is different from DARK: ", BRIGHT <> DARK)
writeln("BRIGHT is the same as RAINBOW: ", BRIGHT = RAINBOW)
end-model

Fair Isaac Corporation Conﬁdential and Proprietary Information

60

Arrays, sets, lists, and records

As one might have expected, this example produces the following output:
BRIGHT is included in RAINBOW: true
RAINBOW is a superset of DARK: false
BRIGHT is different from DARK: true
BRIGHT is the same as RAINBOW: false

8.4

Initializing lists
Lists are not commonly used in the standard formulation of Mathematical Programming problems.
However, this data structure may be useful for the Mosel implementation of some more advanced
solving and programming tasks.

8.4.1

Constant list
If the contents of a list are speciﬁed at the declaration of the list, such as
declarations
L = [1,2,3,4,5,6,7,8,9,10]
end-declarations

we have deﬁned a constant list (its contents cannot be changed). If we want to be able to modify thelist contents subsequently we need to separate the deﬁnition of the list contents from the declaration,
resulting in a dynamic list:
declarations
L: list of integer
end-declarations
L:= [1,2,3,4,5,6,7,8,9,10]

A two-dimensional array of lists may be deﬁned thus (and higher dimensional arrays by analogy):
declarations
M: array(range,set of integer) of list of string
end-declarations
M:: (2..4,1)[['A','B','C'], ['D','E'], ['F','G','H','I']]

8.4.2

List initialization from ﬁleSimilarly to what we have already seen for other data structures, the contents of lists may be initialized
from ﬁle through initializations blocks. For example,
declarations
K: list of integer
N: array(range,set of integer) of list of string
end-declarations
initializations from "listinit.dat"
K N
end-initializations
writeln("K: ", K)
writeln("An entry of N: ", N(5,3))

Assuming the dataﬁle listinit.dat contains these lines

Fair Isaac Corporation Conﬁdential and Proprietary Information

61

Arrays, sets, lists, and records
K: [5 4 3 2 1 1 2 3 4 5]
N: [(3 1) ['B' 'C' 'A']
(5 3) ['D' 'E']
(6 1) ['H' 'I' 'F' 'G']]

we obtain the following output from the model fragment above:
K: [5,4,3,2,1,1,2,3,4,5]
An entry of N: [`D',`E']

8.5

Working with lists

8.5.1

Enumeration
Similarly to the way we have used sets so far, lists may be used as loop indices for enumeration. The
following enumerates a given list L from beginning to end:
declarations
L: list of integer
end-declarations
L:= [1,2,3,4,5]
forall(i in L) writeln(i)Since lists have an ordering we may choose, for instance, to reverse the order of list elements for theenumeration. The model listenum.mos below shows several possibilities for enumerating lists ininverse order: (1) reversing a copy of the list to enumerate, (2) reversing the list to enumerate. In theﬁrst case we obtain the reversed copy of the list with function getreverse, in the second case we
modify the original list by applying to it the procedure reverse.
model "Reversing lists"
declarations
K,L: list of integer
end-declarations
L:= [1,2,3,4,5]! Enumeration in inverse order:! 1. Reversed copy of the list (i.e., no change to 'L')
K:=getreverse(L)forall(i in K) writeln(i)! 2. Reversing the list itself
reverse(L)forall(i in L) writeln(i)end-model

8.5.2

List operators
Lists are composed by concatenating several lists or by truncating their extremities (refered to as head
and tail). The operators += and + serve for concatenating lists. Their inverses (-= and -) may be used
to remove the tail of a list—they will not remove the given sublist if it is not positioned at the end.
The following model listops.mos shows some examples of the use of list operators. Besides theconcatenation operators + and += we also use the aggregate form sum. Another list operator used inthis example is the comparison operator <> (the comparison operator = may also be used with lists).

Fair Isaac Corporation Conﬁdential and Proprietary Information

62

Arrays, sets, lists, and records
model "List operators"
declarations
L,M: list of integer
end-declarations
L:= [1,2,3] + [4,5]; writeln("L (1): ", L)
L+= [6,7,8]; writeln("L (2): ", L)
L-= [1,2,3]; writeln("L (3): ", L)
M:= sum(l in L) [l⁎2]; writeln("M: ", M)
writeln("L and M are different: ", L<>M)
end-model

As can be seen in the output, the list [1,2,3] is not removed from L since it is not located at its tail:L (1): [1,2,3,4,5]
L (2): [1,2,3,4,5,6,7,8]
L (3): [1,2,3,4,5,6,7,8]
M: [2,4,6,8,10,12,14,16]
L and M are different: true

8.5.3

List handling functions
The Mosel subroutines for list handling form two groups, namelyOperations preserving the list they are applied to: retrieving a list element (getelt, getfirst,
getlast), occurrence of an element (findfirst, findlast), retrieving a copy of the head or
tail (gethead, gettail), reversed copy of a list (getreverse)
Operations modifying the list they are applied to: cutting off (=discard) individual elements or thehead or tail (cutelt, cutfirst, cutlast, cuthead, cuttail), splitting off (=retrieve) the heador tail (splithead, splittail), reverse the list (reverse)

The following example listmerge.mos merges two lists of integers K and L, the elements of which
are ordered in increasing order of their values into a new list M that is ordered in the same way. The
elements of the two original lists are added one-by-one to the new list using the concatenation operator+=. Whilst the elements of the list K are simply enumerated, we iteratively split off the ﬁrst elementfrom list L (using splithead with second argument 1 to take away just the ﬁrst list element) so that
this list will be empty at the end of the forall loop. If this is not desired, we need to work with a copyof this list.
model "Merging lists"
declarations
K,L,M: list of integer
end-declarations
K:= [1,4,5,8,9,10,13]
L:= [-1,0,4,6,7,8,9,9,11,11]
forall(k in K) do
while (L<>[] and k >= getfirst(L))
M+= [k]end-doM += splithead(L,1)

writeln(M)end-model

Fair Isaac Corporation Conﬁdential and Proprietary Information

63

Arrays, sets, lists, and records

The resulting list M is:[-1,0,1,4,4,5,6,7,8,8,9,9,9,10,11,11,13]

List handling routines provide a powerful means of programming, illustrated by the following example
euler.mos that constructs a Eulerian circuit for the network shown in Figure 8.1 (thick arrows indicatethat the corresponding arc is to be used twice). This example is an alternative implementation of theEulerian circuit algorithm described in Section 15.4 ‘Gritting roads’ (problem j4grit) of the book
’Applications of optimization with Xpress-MP’.
1

2

3

4

5

6

7

8

9

10

11

12

Figure 8.1: Network forming a Eulerian circuit

A Eulerian circuit is a tour through a network that uses every given arc exactly once. To construct sucha circuit for a given set of arcs we may employ the following algorithm

Choose a start node and add it to the tour.
while there are unused arcs:– Find the ﬁrst node in the tour with unused outgoing arcs.
– Construct a closed subtour starting from this node.
– Insert the new subtour into the main tour.
model "Eulerian circuit"declarations
NODES = 1..12! Set of nodes
UNUSED: array(NODES) of list of integer
TOUR: list of integer
NEWT, TAIL: list of integer
end-declarations
initializations from 'euler.dat'
UNUSED
end-initializations
ct:=sum(i in NODES) getsize(UNUSED(i))
TOUR:=[1]! Choose node 1 as start point

while(ct>0) do! While there are unused arcs:! Find first node in TOUR with unused outgoing arc(s)node:=0
forall(i in TOUR)
if UNUSED(i) <> [] thennode:=i
break
end-ifFair Isaac Corporation Conﬁdential and Proprietary Information

64

Arrays, sets, lists, and records! Insertion position (first occurrence of 'node' in TOUR)
pos:= findfirst(TOUR, node)! Construct a new subtour starting from 'node'
cur:=node! Start with current node
NEWT:=[]
while(UNUSED(cur) <> []) do
NEWT+=splithead(UNUSED(cur),1)! Take first unused arc
cur:=getlast(NEWT)! End point of arc is new current node
end-do! Stop if the subtour is not a closed loop (=> no Eulerian circuit)
if cur<>node then
! Compare start and end of subtour
writeln("Tour cannot be closed")exit(1)
end-if! Add the new subtour to the main journey
TAIL:=splittail(TOUR, -pos)! Split off the tail from main tour
TOUR += NEWT + TAIL
! Attach subtour and tail to main tour
ct -= getsize(NEWT)
end-dowriteln("Tour: ", TOUR)! Print the result

end-model

The data ﬁle euler.dat corresponding to the graph in Figure 8.1 has the following contents:
UNUSED: [(1) [2 5] (2) [3 5 6] (3) [2 4 4] (4) [3 8 8](5) [1 1 6 6] (6) [2 5 7 9 9 10] (7) [3 6 8 11](8) [4 11 12] (9) [5 10] (10) [6 6 7](11) [7 7 10] (12) [11] ]A Eulerian circuit for this data set is the tour
1 → 2 → 6 → 5 → 6 → 7 → 8 → 12 → 11 → 7 → 11 → 10 → 7 → 3 → 4 → 3 → 4 → 8 → 4 → 8 → 11 → 7→ 6 → 9 → 5 → 6 → 9 → 10 → 6 → 10 → 6 → 2 → 3 → 2 → 5 → 1 → 5 → 1

8.6

Records
Records group Mosel objects of different types. They may be used, for instance, to structure the data ofa large-scale model by collecting all information relating to the same object.

8.6.1

Deﬁning records
The deﬁnition of a record has some similarities with the declarations block: it starts with the
keyword record, followed by a list of ﬁeld names and types, and the keyword end-record marks theend of the deﬁnition. The deﬁnition of records must be placed in a declarations block. The
following code extract deﬁnes a record with two ﬁelds (‘name’ and ‘values’).
declarations
R = 1..10
D: record
name: stringvalues: array(R) of real
end-record
end-declarations

We need to deﬁne a name (e.g., ‘mydata’) for the record if we want to be able to refer to it elsewhere in
the model—note that we declare this record as public in order to make all its ﬁelds public (so in

Fair Isaac Corporation Conﬁdential and Proprietary Information

65

Arrays, sets, lists, and records

particular, visible in output display), alternatively, individual ﬁelds can be declared as public. For
example:declarations
R = 1..10
mydata = public record
name: stringvalues: array(R) of real
end-record
D: mydata
A: array(range) of mydata
end-declarations

The ﬁelds of a record are accessed by appending .fieldname to the record, for instance:
D.name:= "D"forall(i in R) D.values(i):= iwriteln("Values of ", D.name, ": ", D.values)
writeln("An entry of A: ", A(1))
writeln("'name' of an entry of A: ", A(4).name)writeln("'values' of an entry of A: ", A(3).values)
writeln("First entry of 'values': ", A(3).values(1))

Note: if a record ﬁeld is an array, the index set(s) of the array must be either constant or be declaredoutside of the record deﬁnition. So, these are valid record deﬁnitions:declarations
R: range
P: recordvalues: array(R) of real
end-record
Q: recordvalues: array(1..10) of real
end-record
end-declarations

whereas this form can not be used:
Q: recordvalues: array(range) of real
end-record

8.6.2

Initialization of records from ﬁleThe contents of a record may be assigned ﬁeldwise within a model as shown above or else be read infrom ﬁle using initializations. The data ﬁle must contain the data entries for the different recordﬁelds in their order of occurrence in the record deﬁnition. An array A of the record type mydata deﬁned
in the previous section is initialized with data from ﬁle in the obvious way (model recorddef.mos):declarations
A: dynamic array(T:range) of mydata
end-declarations
initializations from "recorddef.dat"A
end-initializations
writeln(A(1))
forall(i in T | exists(A(i))) writeln(A(i).name)

Fair Isaac Corporation Conﬁdential and Proprietary Information

66

Arrays, sets, lists, and records

If the data ﬁle recorddef.dat has these contents:
A: [(1) ['A1' [(2) 2 (3) 3 (4) 4] ](3) ['A3' [(3) 6 (6) 9] ](4) ['A4' [5 6 7 8] ](7) ['A7']! Define just the first field(6) [⁎ [(6) 6] ]! Skip the first field]

we obtain the following output (the entry with index 6 is deﬁned but has no name, which accounts forthe empty line between ’A4’ and ’A7’):[name=`A1' values=[0,2,3,4,0,0,0,0,0,0]]
A1
A3
A4
A7

An example of the use of records is the encoding of arcs and associated information such as forrepresenting the network in Figure 8.2.
4

B

F
3

2

2

D

4

8

A

1

5

5

E

7
1

C
Figure 8.2: Network with costs on arcs

A data ﬁle with the network data may look as follows (ﬁle arcs.dat):
ARC: [(1) ["A" "B" 2](2) ["A" "D" 4](3) ["A" "C" 7](4) ["B" "F" 4](5) ["B" "D" 3](6) ["C" "B" 5](7) ["C" "D" 1](8) ["C" "E" 1](9) ["D" "F" 2](10) ["D" "E" 5](11) ["E" "F" 8] ]We may then write our model arcs.mos thus
model "Arcs"
declarations
NODES: set of string
ARC: array(ARCSET:range) of record
Source,Sink: string
Cost: real
end-record
end-declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information! Set of nodes! Arcs:!
Source and sink of arc!
Cost coefficient67Arrays, sets, lists, and records

initializations from 'arcs.dat'
ARC
end-initializations! Calculate the set of nodes
NODES:=union(a in ARCSET) {ARC(a).Source, ARC(a).Sink}
writeln(NODES)
writeln("Average arc cost: ", sum(a in ARCSET) ARC(a).Cost / getsize(ARCSET) )end-model

The record deﬁnition may contain additional ﬁelds (e.g., decision variables) that are not to be initialized
from ﬁle. In this case we need to specify in the initializations block which record ﬁelds are to be
ﬁlled with data.
declarations
NODES: set of string
ARC: array(ARCSET:range) of record
flow: mpvar
Source,Sink: string
Cost: real
end-record
end-declarations! Set of nodes! Arcs:!
Flow quantity!
Source and sink of arc!
Cost coefficient

initializations from 'arcs.dat'
ARC(Source,Sink,Cost)
end-initializations

This functionality can also be used to read separately, and possibly from different sources, the contentsof the record ﬁelds. For instance, the ’Cost’ ﬁeld of our record ARC could be initialized as follows:
initializations from 'arcs.dat'
ARC(Cost) as "COST"
end-initializations

where the data array ’COST’ is given as
COST: [(1) 2 (2) 4 (3) 7 (4) 4 (5) 3 (6) 5(7) 1 (8) 1 (9) 2 (10) 5 (11) 8]

8.7

User types
In a Mosel model, the user may deﬁne new types that will be treated in the same way as the predeﬁned
types of the Mosel language. New types are deﬁned in declarations blocks by specifying a typename, followed by =, and the deﬁnition of the type. The simplest form of a type deﬁnition is to introduce
a new name for an existing type, such as:
declarations
myint = integer
myreal = real
end-declarations

In the section on records above we have already seen an example of a user type deﬁnition for records(where we have named the record ‘mydata’). Another possible use of a user type is as a kind of‘shorthand’ where several (data) arrays have the same structure, such as in the model blend.mos from
Chapter 2, where, instead ofdeclarations

Fair Isaac Corporation Conﬁdential and Proprietary Information

68

Arrays, sets, lists, and records
ORES = 1..2

! Range of ores

COST: array(ORES) of real
AVAIL: array(ORES) of real
GRADE: array(ORES) of real
end-declarations! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)

we could have written
declarations
ORES = 1..2

! Range of ores

myarray = array(ORES) of real

! Define a user type

COST: myarray
AVAIL: myarray
GRADE: myarray
end-declarations! Unit cost of ores
! Availability of ores
! Grade of ores (measured per unit of mass)

without making any other modiﬁcations to the model.

Fair Isaac Corporation Conﬁdential and Proprietary Information

69

CHAPTER 9

Functions and procedures
When programs grow larger than the small examples presented so far, it becomes necessary to
introduce some structure that makes them easier to read and to maintain. Usually, this is done by
dividing the tasks that have to be executed into subtasks which may again be subdivided, andindicating the order in which these subtasks have to be executed and which are their activation
conditions. To facilitate this structured approach, Mosel provides the concept of subroutines. Using
subroutines, longer and more complex programs can be broken down into smaller subtasks that areeasier to understand and to work with. Subroutines may be employed in the form of procedures orfunctions. Procedures are called as a program statement, they have no return value, functions must be
called in an expression that uses their return value.
Mosel provides a set of predeﬁned subroutines (for a comprehensive documentation the reader isreferred to the Mosel Reference Manual), and it is possible to deﬁne new functions and procedures
according to the needs of a speciﬁc program. A procedure that has occured repeatedly in this document
is writeln. Typical examples of functions are mathematical functions like abs, floor, ln, sin etc.

9.1

Subroutine deﬁnition
User deﬁned subroutines in Mosel have to be marked with procedure / end-procedure andfunction / end-function respectively. The return value of a function has to be assigned toreturned as shown in the following example (model subrout.mos).
model "Simple subroutines"
declarations
a:integer
end-declarations
function three:integer
returned := 3
end-function
procedure print_start
writeln("The program starts here.")end-procedure
print_start
a:=threewriteln("a = ", a)end-modelThis program will produce the following output:
The program starts here.
a = 3

Fair Isaac Corporation Conﬁdential and Proprietary Information

70

Functions and procedures

9.2

Parameters
In many cases, the actions to be performed by a procedure or the return value expected from a function
depend on the current value of one or several objects in the calling program. It is therefore possible topass parameters into a subroutine. The (list of) parameter(s) is added in parantheses behind the nameof the subroutine:function times_two(b:integer):integer
returned := 2⁎b
end-function

The structure of subroutines being very similar to the one of model, they may also include
declarations sections for declaring local parameters that are only valid in the corresponding
subroutine. It should be noted that such local parameters may mask global parameters within thescope of a subroutine, but they have no effect on the deﬁnition of the global parameter outside of thesubroutine as is shown below in the extension of the example ‘Simple subroutines’. As in otherprogramming languages, it is not possible to redeﬁne function/procedure parameters in thecorresponding subroutine (the declaration of local parameters must not hide these parameters). Mosel
considers this as a mistake and prints an error message during compilation.
model "Simple subroutines"
declarations
a:integer
end-declarations
function three:integer
returned := 3
end-function
function times_two(b:integer):integer
returned := 2⁎b
end-function
procedure print_start
writeln("The program starts here.")end-procedure
procedure hide_a_1
declarations
a: integer
end-declarations
a:=7
writeln("Procedure hide_a_1: a = ", a)end-procedure
procedure hide_a_2(a:integer)
writeln("Procedure hide_a_2: a = ", a)end-procedure
procedure hide_a_3(a:integer)
declarations
a: integer
end-declarations
a := 15
writeln("Procedure hide_a_3: a = ", a)end-procedure
print_start
a:=threewriteln("a = ", a)a:=times_two(a)Fair Isaac Corporation Conﬁdential and Proprietary Information

71

Functions and procedures
writeln("a = ", a)hide_a_1
writeln("a = ", a)hide_a_2(-10)
writeln("a = ", a)hide_a_3(a)writeln("a = ", a)end-model

During the compilation we get the error
Mosel: E-165 at (34,4) of `subrout.mos': Declaration of `a' hides a parameter.

This is due to the redeﬁnition of a that is passed as an argument into procedure hide_a_3 and also
appears in the declarations of this subroutine. We need to modify the deﬁnition of this procedure tocorrect this error, for example by renaming the subroutine argument:
procedure hide_a_3(aa:integer)

The program then results in the following output:
The program starts here.
a = 3
a = 6
Procedure hide_a_1: a = 7
a = 6
Procedure hide_a_2: a = -10
a = 6
Procedure hide_a_3: a = 15
a = 6

9.3

Recursion
The following example (model lcdiv2.mos) returns the largest common divisor of two numbers, justlike the example ‘Lcdiv1’ in the previous chapter. This time we implement this task using recursivefunction calls, that is, from within function lcdiv we call again function lcdiv.
model Lcdiv2
function lcdiv(A,B:integer):integerif(A=B) thenreturned:=A
elif(A>B) thenreturned:=lcdiv(B,A-B)
else
returned:=lcdiv(A,B-A)
end-ifend-function
declarations
A,B: integer
end-declarations
write("Enter two integer numbers:\nreadln(A)write(" B: ")readln(B)A: ")writeln("Largest common divisor: ", lcdiv(A,B))
end-model

Fair Isaac Corporation Conﬁdential and Proprietary Information

72

Functions and proceduresThis example uses a simple recursion (a subroutine calling itself). In Mosel, it is also possible to use
cross-recursion, that is, subroutine A calls subroutine B which again calls A. The only pre-requisite isthat any subroutine that is called prior to its deﬁnition must be declared before it is called by using theforward statement (see below).

9.4 forward
A subroutine has to be ‘known’ at the place where it is called in a program. In the preceding examples
we have deﬁned all subroutines at the start of the programs but this may not always be feasible ordesirable. Mosel therefore enables the user to declare a subroutine separately from its deﬁnition byusing the keyword forward. The declaration of of a subroutine states its name, the parameters (typeand name) and, in the case of a function, the type of the return value. The deﬁnition that must follow
later in the program contains the body of the subroutine, that is, the actions to be executed by thesubroutine.
The following example (model qsort1.mos) implements a quick sort algorithm for sorting a randomly
generated array of numbers into ascending order—please note that the implementation discussed here
is merely provided as a programming example, we would generally recommend that you use the qsort
routine of the Mosel module mmsystem in your Mosel programs. The procedure qsort that starts thesorting algorithm is deﬁned at the very end of the program, it therefore needs to be declared at the
beginning, before it is called. Procedure qsort_start calls the main sorting routine, qsort. Since thedeﬁnition of this procedure precedes the place where it is called there is no need to declare it (but it still
could be done). Procedure qsort calls yet again another subroutine, swap.
The idea of the quick sort algorithm is to partition the array that is to be sorted into two parts. The ‘left’part containing all values smaller than the partitioning value and the ‘right’ part all the values that arelarger than this value. The partitioning is then applied to the two subarrays, and so on, until all values
are sorted.
model "Quick sort 1"
parameters
LIM=50end-parameters
forward procedure qsort_start(L:array(range) of integer)
declarations
T:array(1..LIM) of integer
end-declarations
forall(i in 1..LIM) T(i):=round(.5+random⁎LIM)
writeln(T)qsort_start(T)writeln(T)! Swap the positions of two numbers in an arrayprocedure swap(L:array(range) of integer,i,j:integer)k:=L(i)
L(i):=L(j)L(j):=k
end-procedure! Main sorting routine
procedure qsort(L:array(range) of integer,s,e:integer)v:=L((s+e) div 2)! Determine the partitioning value
i:=s; j:=erepeat! Partition into two subarrays
while(L(i)<v) i+=1
while(L(j)>v) j-=1
if i<j then

Fair Isaac Corporation Conﬁdential and Proprietary Information

73

Functions and procedures
swap(L,i,j)
i+=1; j-=1
end-ifuntil i>=j

! Recursively sort the two subarrays
if j<e and s<j then qsort(L,s,j); end-if
if i>s and i<e then qsort(L,i,e); end-if
end-procedure! Start of the sorting processprocedure qsort_start(L:array(r:range) of integer)
qsort(L,getfirst(r),getlast(r))
end-procedure
end-model

The quick sort example above demonstrates typical uses of subroutines, namely grouping actions thatare executed repeatedly (qsort) and isolating subtasks (swap) in order to structure a program andincrease its readability.
The calls to the procedures in this example are nested (procedure swap is called from qsort which iscalled from qsort_start): in Mosel there is no limit as to the number of nested calls to subroutines(it is not possible, though, to deﬁne subroutines within a subroutine).

9.5

Overloading of subroutines
In Mosel, it is possible to re-use the names of subroutines, provided that every version has a different
number and/or types of parameters. This functionality is commonly referred to as overloading.
An example of an overloaded function in Mosel is getsol: if a variable is passed as a parameter it
returns its solution value, if the parameter is a constraint the function returns the evaluation of thecorresponding linear expression using the current solution.
Function abs (for obtaining the absolute value of a number) has different return types depending on
the type of the input parameter: if an integer is input it returns an integer value, if it is called with a realvalue as input parameter it returns a real.
Function getcoeff is an example of a function that takes different numbers of parameters: if calledwith a single parameter (of type linctr) it returns the constant term of the input constraint, if aconstraint and a variable are passed as parameters it returns the coefﬁcient of the variable in the given
constraint.
The user may deﬁne (additional) overloaded versions of any subroutines deﬁned by Mosel as well as
for his own functions and procedures. Note that it is not possible to overload a function with aprocedure and vice versa.
Using the possibility to overload subroutines, we may rewrite the preceding example ‘Quick sort’ asfollows (model qsort2.mos).
model "Quick sort 2"
parameters
LIM=50end-parameters
forward procedure qsort(L:array(range) of integer)
declarations
T:array(1..LIM) of integer
end-declarations
forall(i in 1..LIM) T(i):=round(.5+random⁎LIM)

Fair Isaac Corporation Conﬁdential and Proprietary Information

74

Functions and procedures
writeln(T)qsort(T)writeln(T)procedure swap(L:array(range) of integer,i,j:integer)(...)(same procedure body as in the preceding example)
end-procedure
procedure qsort(L:array(range) of integer,s,e:integer)(...)(same procedure body as in the preceding example)
end-procedure! Start of the sorting processprocedure qsort(L:array(r:range) of integer)
qsort(L,getfirst(r),getlast(r))
end-procedure
end-model

The procedure qsort_start is now also called qsort. The procedure bearing this name in the ﬁrst
implementation keeps its name too; it has got two additional parameters which sufﬁce to ensure thatthe right version of the procedure is called. To the contrary, it is not possible to give procedure swap thesame name qsort because it takes exactly the same parameters as the original procedure qsort andhence it would not be possible to differentiate between these two procedures any more.

Fair Isaac Corporation Conﬁdential and Proprietary Information75

CHAPTER 10

Output10.1

Producing formatted output
In some of the previous examples the procedures write and writeln have been used for displaying
data, solution values and some accompanying text. To produce better formatted output, theseprocedures can be combined with the formatting procedure strfmt. In its simplest form, strfmt’s
second argument indicates the (minimum) space reserved for writing the ﬁrst argument and its
placement within this space (negative values mean left justiﬁed printing, positive right justiﬁed). When
writing a real, a third argument may be used to specify the maximum number of digits after thedecimal point.
For example, if ﬁle fo.mos contains
model FOparameters
r = 1.0
i = 0
end-parameters! A real! An integer

writeln("i is ", i)writeln("i is ", strfmt(i,6) )writeln("i is ", strfmt(i,-6) )writeln("r is ", r)writeln("r is ", strfmt(r,6) )writeln("r is ",strfmt(r,10,4) )end-model

and we run Mosel thus:
mosel exec fo 'i=123, r=1.234567'

we get output
i is 123i is
123i is 123
r is 1.23457
r is 1.23457
r is
1.2346

The following example (model transport2.mos) prints out the solution of model ‘Transport’ (Section
3.2) in table format. The reader may be reminded that the objective of this problem is to compute theproduct ﬂows from a set of plants (PLANT) to a set of sales regions (REGION) so as to minimize thetotal cost. The solution needs to comply with the capacity limits of the plants (PLANTCAP) and satisfy
the demand DEMAND of all regions.
procedure print_table

Fair Isaac Corporation Conﬁdential and Proprietary Information76

Output
declarations
rsum: array(REGION) of integer
psum,ct,iflow: integer
end-declarations! Auxiliary data table for printing! Counters! Print heading and the first line of the table
writeln("\nProduct Distribution\n", "="⁎20)
writeln(strfmt("Sales Region",48))
write(strfmt("",15), "| ")forall(r in REGION) write(strfmt(r,9))
writeln(" |", strfmt("TOTAL",6), " Capacity")
writeln("-"⁎80)! Print the solution values of the flow variables and! calculate totals per region and per plant

ct:=0
forall(p in PLANT, ct as counter) do
if ct=2 thenwrite(" Plant ", strfmt(p,-8), "|")
else
write("", strfmt(p,-8), "|")
end-ifpsum:=0
forall(r in REGION) do
iflow:=integer(getsol(flow(p,r)))psum += iflow
rsum(r) += iflow
if iflow<>0 thenwrite(strfmt(iflow,9))
else
write("-- ")end-ifend-dowriteln(" |", strfmt(psum,6), strfmt(integer(PLANTCAP(p)),8))
end-do! Print the column totals
writeln("-"⁎80)
write(strfmt(" TOTAL",-15), "|")
prsum:=0
forall(r in REGION) write(strfmt(rsum(r),9))writeln(" |", strfmt(sum(r in REGION) rsum(r),6))! Print demand of every region
write(strfmt(" Demand",-15), "|")
forall(r in REGION) write(strfmt(integer(DEMAND(r)),9))! Print objective function value
writeln("\n\nTotal cost of distribution = ", strfmt(getobjval/1e6,0,3)," million.")end-procedure

Notice the shorthand "-"⁎80 meaning that the string ’-’ is repeated 80 times. This functionality isprovided by the module mmsystem, however, it is generally more efﬁcient to work with the type text
for such string operations (see Section 17.6).
With the data from Chapter 3 the procedure print_table produces the following output:
Product Distribution====================

Sales Region| Scotland
North
SWest
SEast Midlands | TOTAL Capacity-------------------------------------------------------------------------------Corby
|
-80
-920
2000 | 3000
3000
Plant Deeside |
-1450
1000
-250 | 2700
2700
Glasgow |28401270---| 4110
4500
Oxford |--16001900
500 | 4000
4000

Fair Isaac Corporation Conﬁdential and Proprietary Information

77Output-------------------------------------------------------------------------------TOTAL
|
2840
2800
2600
2820
2750 | 13810
Demand
|
2840
2800
2600
2820
2750
Total cost of distribution = 81.018 million.

10.2

File output
If we do not want the output of procedure print_tab in the previous section to be displayed on screen
but to be saved in the ﬁle out.txt, we simply open the ﬁle for writing at the beginning of the procedureby addingfopen("out.txt",F_OUTPUT)

before the ﬁrst writeln statement, and close it at the end of the procedure, after the last writeln
statement withfclose(F_OUTPUT)

If we do not want any existing contents of the ﬁle out.txt to be deleted, so that the result table isappended to the end of the ﬁle, we need to write the following for opening the ﬁle (closing it the same
way as before):fopen("out.txt",F_OUTPUT+F_APPEND)

As with input of data from ﬁle, there are several ways of outputting data to a ﬁle in Mosel. The following
example demonstrates three different ways of writing the contents of an array A to a ﬁle. The lastsection (10.2.4) shows how to proceed if the data is not readily available in the form of an array but
results from the evaluation of an expression (e.g., solution values, function calls).

10.2.1

Data output with initializations to
The ﬁrst method uses the initializations block for creating or updating a ﬁle in Mosel’s
initializations format.
model "Trio output (1)"declarations
A: array(-1..1,5..7) of real
end-declarations
A :: [ 2, 4, 6,
12, 14, 16,
22, 24, 26]! First method: use an initializations block
initializations to "out_1.dat"
A as "MYOUT"
end-initializations
end-model

File out_1.dat will contain the following:'MYOUT': [2 4 6 12 14 16 22 24 26]

If this ﬁle contains already a data entry MYOUT, it is replaced with this output without modifying ordeleting any other contents of this ﬁle. Otherwise, the output is appended at the end of it.
Note: For solution output with initializations to please see Section 10.2.4 below.

Fair Isaac Corporation Conﬁdential and Proprietary Information78

Output

10.2.2

Data output with writeln
As mentioned above, we may create freely formatted output ﬁles by redirecting the output of write
and writeln statements to a ﬁle:model "Trio output (2)"declarations
A: array(-1..1,5..7) of real
end-declarations
A :: [ 2, 4, 6,
12, 14, 16,
22, 24, 26]! Second method: use the built-in writeln function
fopen("out_2.dat", F_OUTPUT)
forall(i in -1..1, j in 5..7)
writeln('A_out(', i, ' and ', j, ') = ', A(i,j))fclose(F_OUTPUT)
end-model

The nicely formatted output to out_2.dat results in the following:
A_out(-1 and 5) = 2
A_out(-1 and 6) = 4
A_out(-1 and 7) = 6
A_out(0 and 5) = 12
A_out(0 and 6) = 14
A_out(0 and 7) = 16
A_out(1 and 5) = 22
A_out(1 and 6) = 24
A_out(1 and 7) = 26

10.2.3

Data output with diskdata
As a third possibility, one may use the diskdata subroutine from module mmetc to write out comma
separated value (CSV) ﬁles.
model "Trio output (3)"uses "mmetc"
declarations
A: array(-1..1,5..7) of real
end-declarations
A :: [ 2, 4, 6,
12, 14, 16,
22, 24, 26]! Third method: use diskdata
diskdata(ETC_OUT+ETC_SPARSE,"out_3.dat", A)
end-model

The output with diskdata simply prints the contents of the array to out_3.dat, with option
ETC_SPARSE each entry is preceded by the corresponding indices:-1,5,2-1,6,4-1,7,6
0,5,12
0,6,14
0,7,16
1,5,22

Fair Isaac Corporation Conﬁdential and Proprietary Information

79

Output
1,6,24
1,7,26

Without option ETC_SPARSE out_3.dat looks as follows:
2,4,6
12,14,16
22,24,26

Instead of using the diskdata subroutine, we may equally use the diskdata I/O driver that is deﬁned
by the same module, mmetc. In the example above we replace the diskdata statement by thefollowing initializations to block.
[ initializations to 'mmetc.diskdata:'
A as 'sparse,out_3.dat'
end-initializations

10.2.4

Solution output with initializations to
In the previous examples we have seen how to write out to a ﬁle the contents of a data array deﬁned in
Mosel. The free format output with write/writeln can be applied to any type of expression.
However, if we wish to use the initializations to functionality for writing out, for instance, thesolution values after an optimization run or the result of a Mosel function we need to proceed in aslightly different way from what we have seen so far.
There are two options:
1. Save the solution values or results into a new Mosel object and work with this copy for the ﬁleoutput. For example,
declarations
x: mpvar
x_sol: real
y: array(R) of mpvar
y_sol: array(R) of real
end-declarations...
! Define and solve an optimization problem
x_sol:= x.sol! Retrieve the solution values
forall(i in R) y_sol(i):= y(i).sol
initializations to "out.txt"
x_sol
y_sol
end-initializations

2. Use the keyword evaluation in the initializations block.
declarations
x: mpvar
y: array(R) of mpvar
end-declarations...
! Define and solve an optimization problem
initializations to "out.txt"
evaluation of x.sol as "x_sol"
evaluation of array(i in R) y(i).sol as "y_sol"
end-initializations

The array construct is used in the model extract above to generate a new array ’on the ﬂy’. Its use issimilar to aggregate operators such as sum or union.
The use of the marker evaluation of is not restricted to solution values; it allows you to work withany type of expression directly in the initializations block, including results of Mosel functions orFair Isaac Corporation Conﬁdential and Proprietary Information

80Output

computations as shown in the following example initeval.mos. This model writes out detailed
results for our introductory Chess example (see Section 1.3).
model "Evaluations"uses "mmxprs"
declarations
small,large: mpvar
end-declarations! Decision variables: produced quantities

Profit:= 5⁎small + 20⁎large
Lathe:=
3⁎small + 2⁎large <= 160
Boxwood:=small + 3⁎large <= 200
small is_integer; large is_integer! Objective function! Lathe-hours! kg of boxwood! Integrality constraints

maximize(Profit)! Solve the problem

initializations to "chessout.txt"
evaluation of getparam("XPRS_mipstatus") as "Status"
evaluation of getobjval as "Objective"
evaluation of small.sol as "small_sol"
evaluation of getsol(large) as "large_sol"
evaluation of Lathe.slack as "Spare time"evaluation of Boxwood.act as "Used wood"
evaluation of Boxwood.act-200 as "Spare wood"
evaluation of [ small.sol, large.sol ] as "x_sol"
end-initializations
end-model

The resulting output ﬁle chessout.txt has the following contents:'Status': 6'Objective': 1330
'small_sol': 2'large_sol': 66'Spare time': 22'Used wood': 200'Spare wood': 0'x_sol': [2 66]

10.3

Real number format
Whenever output is printed (including matrix export to a ﬁle) Mosel uses the standard representation of
ﬂoating point numbers of the operating system (C format %g). This format may apply rounding whenprinting large numbers or numbers with many decimals. It may therefore sometimes be preferable tochange the output format to a ﬁxed format to see the exact results of an optimization run or to produce
a matrix output ﬁle with greater accuracy. Consider the following example (model numformat.mos):model "Formatting numbers"
parameters
a = 12345000.0
b = 12345048.9
c = 12.000045
d = 12.0
end-parameters
writeln(a, "", b, "", c, "", d)setparam("REALFMT", "%1.6f")
writeln(a, " ", b, " ", c, "end-model", d)Fair Isaac Corporation Conﬁdential and Proprietary Information81

Output

This model produces the following output.
1.2345e+07 1.2345e+07 12 1212345000.000000 12345048.900000

12.000045

12.000000

That is, with the default printing format it is not possible to distinguish between a and b or to see that c
is not an integer. After setting a ﬁxed format with 6 decimals all these numbers are output with their
exact values.

Fair Isaac Corporation Conﬁdential and Proprietary Information82

CHAPTER 11

More about Integer Programming
This chapter presents two applications to (Mixed) Integer Programming of the programming facilities
in Mosel that have been introduced in the previous chapters.

11.1

Cut generation
Cutting plane methods add constraints (cuts) to the problem that cut off parts of the convex hull of theinteger solutions, thus drawing the solution of the LP relaxation closer to the integer feasible solutions
and improving the bound provided by the solution of the relaxed problem.
The Xpress Optimizer provides automated cut generation (see the optimizer documentation for
details). To show the effects of the cuts that are generated by our example we switch off theautomated cut generation.

11.1.1

Example problem
The problem we want to solve is the following: a large company is planning to outsource the cleaningof its ofﬁces at the least cost. The NSITES ofﬁce sites of the company are grouped into areas (set
AREAS = {1, ..., NAREAS}). Several professional cleaning companies (set
CONTR = {1, ..., NCONTRACTORS}) have submitted bids for the different sites, a cost of 0 in the data
meaning that a contractor is not bidding for a site.
To avoid being dependent on a single contractor, adjacent areas have to be allocated to different
contractors. Every site s (s in SITES = {1, ..., NSITES}) is to be allocated to a single contractor, but there
may be between LOWCONa and UPPCONa contractors per area a.

11.1.2Model formulation
For the mathematical formulation of the problem we introduce two sets of variables:
cleancs indicates whether contractor c is cleaning site sallocca indicates whether contractor c is allocated any site in area a
The objective to minimize the total cost of all contracts is as follows (where PRICEsc is the price persite and contractor):
X
X
minimize
PRICEsc · cleancsc∈CONTR s∈SITES

We need the following three sets of constraints to formulate the problem:
1. Each site must be cleaned by exactly one contractor.
X∀s ∈ SITES :cleancs = 1
c∈CONTR

Fair Isaac Corporation Conﬁdential and Proprietary Information

83More about Integer Programming

2. Adjacent areas must not be allocated to the same contractor.
∀c ∈ CONTR, a, b ∈ AREAS, a > b and ADJACENTab = 1 : allocca + alloccb ≤ 1
3. The lower and upper limits on the number of contractors per area must be respected.
X
allocca ≥ LOWCONa∀a ∈ AREAS :c∈CONTR∀a ∈ AREAS :X

allocca ≤ UPPCONac∈CONTR

To express the relation between the two sets of variables we need more constraints: a contractor c isallocated to an area a if and only if he is allocated a site s in this area, that is, yca is 1 if and only if somexcs (for a site s in area a) is 1. This equivalence is expressed by the following two sets of constraints,
one for each sense of the implication (AREAs is the area a site s belongs to and NUMSITEa the numberof sites in area a):X∀c ∈ CONTR, a ∈ AREAS : allocca ≤cleancss∈SITESAREAs =a∀c ∈ CONTR, a ∈ AREAS : allocca ≥X
1·cleancsNUMSITEa s∈SITES
AREAs =a11.1.3

Implementation
The resulting Mosel program is the following model clean.mos. The variables cleancs are deﬁned as adynamic array and are only created if contractor c bids for site s (that is, PRICEsc > 0 or, taking into
account inaccuracies in the data, PRICEsc > 0.01).
Another implementation detail that the reader may notice is the separate initialization of the arraysizes: we are thus able to create all arrays with ﬁxed sizes (with the exception of the previously
mentioned array of variables that is explicitly declared dynamic). This allows Mosel to handle them in amore efﬁcient way.
model "Office cleaning"uses "mmxprs","mmsystem"
declarations
PARAM: array(1..3) of integer
end-declarations
initializations from 'clparam.dat'
PARAM
end-initializations
declarations
NSITES = PARAM(1)NAREAS = PARAM(2)NCONTRACTORS = PARAM(3)AREAS = 1..NAREAS
CONTR = 1..NCONTRACTORSSITES = 1..NSITESAREA: array(SITES) of integer
NUMSITE: array(AREAS) of integer
LOWCON: array(AREAS) of integer! Number of sites! Number of areas (subsets of sites)! Number of contractors! Area site is in! Number of sites in an area
! Lower limit on the number of! contractors per area
UPPCON: array(AREAS) of integer! Upper limit on the number of! contractors per area
ADJACENT: array(AREAS,AREAS) of integer! 1 if areas adjacent, 0 otherwise

Fair Isaac Corporation Conﬁdential and Proprietary Information

84

More about Integer Programming
PRICE: array(SITES,CONTR) of real

! Price per contractor per siteclean: dynamic array(CONTR,SITES) of mpvar ! 1 iff contractor c cleans site salloc: array(CONTR,AREAS) of mpvar! 1 iff contractor allocated to a site
! in area a
end-declarations
initializations from 'cldata.dat'[NUMSITE,LOWCON,UPPCON] as 'AREA'
ADJACENT
PRICE
end-initializations
ct:=1
forall(a in AREAS) do
forall(s in ct..ct+NUMSITE(a)-1)
AREA(s):=act+= NUMSITE(a)end-doforall(c in CONTR, s in SITES | PRICE(s,c) > 0.01) create(clean(c,s))! Objective: Minimize total cost of all cleaning contracts
Cost:= sum(c in CONTR, s in SITES) PRICE(s,c)⁎clean(c,s)! Each site must be cleaned by exactly one contractor
forall(s in SITES) sum(c in CONTR) clean(c,s) = 1! Ban same contractor from serving adjacent areas
forall(c in CONTR, a,b in AREAS | a > b and ADJACENT(a,b) = 1)
alloc(c,a) + alloc(c,b) <= 1
! Specify lower & upper limits on contracts per area
forall(a in AREAS | LOWCON(a)>0)
sum(c in CONTR) alloc(c,a) >= LOWCON(a)forall(a in AREAS | UPPCON(a)<NCONTRACTORS)
sum(c in CONTR) alloc(c,a) <= UPPCON(a)! Define alloc(c,a) to be 1 iff some clean(c,s)=1 for sites s in area aforall(c in CONTR, a in AREAS) do
alloc(c,a) <= sum(s in SITES| AREA(s)=a) clean(c,s)
alloc(c,a) >= 1.0/NUMSITE(a) ⁎ sum(s in SITES| AREA(s)=a) clean(c,s)
end-doforall(c in CONTR) do
forall(s in SITES) clean(c,s) is_binary
forall(a in AREAS) alloc(c,a) is_binary
end-dominimize(Cost)
end-model! Solve the MIP problem

In the preceding model, we have chosen to implement the constraints that force the variables allocca to
become 1 whenever a variable cleancs is 1 for some site s in area a in an aggregated way (this type of
constraint is usually referred to as Multiple Variable Lower Bound, MVLB, constraints). Instead offorall(c in CONTR, a in AREAS)
alloc(c,a) >= 1.0/NUMSITE(a) ⁎ sum(s in SITES| AREA(s)=a) clean(c,s)

we could also have used the stronger formulation
forall(c in CONTR, s in SITES)
alloc(c,AREA(s)) >= clean(c,s)

but this considerably increases the total number of constraints. The aggregated constraints are
sufﬁcient to express this problem, but this formulation is very loose, with the consequence that the

Fair Isaac Corporation Conﬁdential and Proprietary Information

85

More about Integer Programming

solution of the LP relaxation only provides a very bad approximation of the integer solution that wewant to obtain. For large data sets the Branch-and-Bound search may therefore take a long time.

11.1.4Cut-and-Branch
To improve this situation without blindly adding many unnecessary constraints, we implement a cut
generation loop at the top node of the search that only adds those constraints that are violated be thecurrent LP solution.
The cut generation loop (procedure top_cut_gen) performs the following steps:solve the LP and save the basis



get the solution values

identify violated constraints and add them to the problem

load the modiﬁed problem and load the previous basis
procedure top_cut_gen
declarations
MAXCUTS = 2500
! Max no. of constraints added in total
MAXPCUTS = 1000
! Max no. of constraints added per pass
MAXPASS = 50
! Max no. passes
ncut, npass, npcut: integer! Counters for cuts and passes
feastol: real
! Zero tolerance
solc: array(CONTR,SITES) of real
! Sol. values for variables `clean'sola: array(CONTR,AREAS) of real
! Sol. values for variables `alloc'
objval,starttime: real
cut: array(range) of linctr
bas: basis! LP basis
end-declarations
starttime:=gettime
setparam("XPRS_CUTSTRATEGY", 0)
setparam("XPRS_PRESOLVE", 0)feastol:= getparam("XPRS_FEASTOL")
setparam("ZEROTOL", feastol ⁎ 10)
ncut:=0
npass:=0! Disable automatic cuts
! Switch presolve off! Get the solver zero tolerance! Set the comparison tolerance of Mosel

while (ncut<MAXCUTS and npass<MAXPASS) do
npass+=1
npcut:= 0
minimize(XPRS_LIN, Cost)! Solve the LP
if (npass>1 and objval=getobjval) then break; end-if
savebasis(bas)! Save the current basis
objval:= getobjval! Get the objective value
forall(c in CONTR) do! Get the solution values
forall(a in AREAS) sola(c,a):=getsol(alloc(c,a))forall(s in SITES) solc(c,s):=getsol(clean(c,s))
end-do! Search for violated constraints and add them to the problem:
forall(c in CONTR, s in SITES)
if solc(c,s) > sola(c,AREA(s)) thencut(ncut):= alloc(c,AREA(s)) >= clean(c,s)
ncut+=1
npcut+=1
if (npcut>MAXPCUTS or ncut>MAXCUTS) then break 2; end-if
end-ifwriteln("Pass ", npass, " (", gettime-starttime, " sec), objective value ",objval, ", cuts added: ", npcut, " (total ", ncut,")")

Fair Isaac Corporation Conﬁdential and Proprietary Information

86More about Integer Programming
if npcut=0 then
break
else
loadprob(Cost)
loadbasis(bas)end-ifend-do! Reload the problem! Load the saved basis! Display cut generation statuswrite("Cut phase completed: ")if (ncut >= MAXCUTS) then writeln("space for cuts exhausted")
elif (npass >= MAXPASS) then writeln("maximum number of passes reached")
else writeln("no more violations or no improvement to objective")
end-ifend-procedure

Assuming we add the deﬁnition of procedure top_cut_gen to the end of our model, we need to add
its declaration at the beginning of the model:
forward procedure topcutgen

and the call to this function immediately before the optimization:
top_cut_gen
minimize(Cost)! Constraint generation at top node
! Solve the MIP problem

Since we wish to use our own cut strategy, we switch off the default cut generation in Xpress Optimizer:
setparam("XPRS_CUTSTRATEGY", 0)We also turn the presolve off since we wish to access the solution to the original problem after solving
the LP-relaxations:setparam("XPRS_PRESOLVE", 0)

11.1.5Comparison tolerance
In addition to the parameter settings we also retrieve the feasibility tolerance used by Xpress Optimizer:
the solver works with tolerance values for integer feasibility and solution feasibility that are typically ofthe order of 10–6 by default. When evaluating a solution, for instance by performing comparisons, it isimportant to take into account these tolerances.
After retrieving the feasibility tolerance of the solver we set the comparison tolerance of Mosel(ZEROTOL) to this value. This means, for example, the test x = 0 evaluates to true if x lies between-ZEROTOL and ZEROTOL, x ≤ 0 is true if the value of x is at most ZEROTOL, and x > 0 is fulﬁlled if x is
greater than ZEROTOL.
Comparisons in Mosel always use a tolerance, with a very small default value. By resetting thisparameter to the solver feasibility tolerance Mosel evaluates solution values just like Xpress Optimizer.

11.1.6

Branch-and-Cut
The cut generation loop presented in the previous subsection only generates violated inqualities at the
top node before entering the Branch-and-Bound search and adds them to the problem in the form of
additional constraints. We may do the same using the cut manager of Xpress Optimizer. In this case,the violated constraints are added to the problem via the cut pool. We may even generate and add cuts
during the Branch-and-Bound search. A cut added at a node using addcuts only applies to this nodeand its descendants, so one may use this functionality to deﬁne local cuts (however, in our example, allgenerated cuts are valid globally).
The cut manager is set up with a call to procedure tree_cut_gen before starting the optimization

Fair Isaac Corporation Conﬁdential and Proprietary Information

87More about Integer Programming(preceded by the declaration of the procedure using forward earlier in the program). To avoid
initializing the solution arrays and the feasibility tolerance repeatedly, we now turn these into globallydeﬁned objects:
declarations
feastol: real
solc: array(CONTR,SITES) of real
sola: array(CONTR,AREAS) of real
end-declarations! Zero tolerance! Sol. values for variables `clean'! Sol. values for variables `alloc'

tree_cut_gen
minimize(Cost)! Set up cut generation in B&B tree
! Solve the MIP problem

As we have seen before, procedure tree_cut_gen disables the default cut generation and turns
presolve off. It also indicates the number of extra rows to be reserved in the matrix for the cuts we are
generating:procedure tree_cut_gen
setparam("XPRS_CUTSTRATEGY", 0)
setparam("XPRS_PRESOLVE", 0)
setparam("XPRS_EXTRAROWS", 5000)! Disable automatic cuts
! Switch presolve off! Reserve extra rows in matrix

feastol:= getparam("XPRS_FEASTOL")
setparam("zerotol", feastol ⁎ 10)! Get the zero tolerance! Set the comparison tolerance of Mosel

setcallback(XPRS_CB_CUTMGR, "cb_node")
end-procedure

The last line of this procedure deﬁnes the cut manager entry callback function that will be called by the
optimizer from every node of the Branch-and-Bound search tree. This cut generation routine (function
cb_node) performs the following steps:


get the solution values

identify violated inequalities and add them to the problem

It is implemented as follows (we restrict the generation of cuts to the ﬁrst three levels, i.e. depth <4,of the search tree):public function cb_node:boolean
declarations
ncut: integer
cut: dynamic array(range) of linctr
cutid: dynamic array(range) of integer
type: dynamic array(range) of integer
end-declarations
returned:=false! Counters for cuts
! Cuts! Cut type identification! Cut constraint type

! Call this function once per node

depth:=getparam("XPRS_NODEDEPTH")
node:=getparam("XPRS_NODES")
if depth<4 thenncut:=0! Get the solution values
forall(c in CONTR) do
forall(a in AREAS) sola(c,a):=getsol(alloc(c,a))forall(s in SITES) solc(c,s):=getsol(clean(c,s))
end-do! Search for violated constraints

Fair Isaac Corporation Conﬁdential and Proprietary Information

88More about Integer Programming
forall(c in CONTR, s in SITES)
if solc(c,s) > sola(c,AREA(s)) thencut(ncut):= alloc(c,AREA(s)) - clean(c,s)
cutid(ncut):= 1
type(ncut):= CT_GEQ
ncut+=1
end-if! Add cuts to the problem
if ncut>0 thenreturned:=true! Call this function again
addcuts(cutid, type, cut);writeln("Cuts added : ", ncut, " (depth ", depth, ", node ", node,", obj. ", getparam("XPRS_LPOBJVAL"), ")")
end-ifend-ifend-function

The prototype of this function is prescribed by the type of the callback (see the Xpress Optimizer
Reference Manual and the chapter on mmxprs in the Mosel Language Reference Manual). We declare
the function as public to make sure that our model continues to work if it is compiled with the -s(strip) option. At every node this function is called repeatedly, followed by a re-solution of the current
LP, as long as it returns true.

11.2

Column generation
The technique of column generation is used for solving linear problems with a huge number of variables
for which it is not possible to generate explicitly all columns of the problem matrix. Starting with a very
restricted set of columns, after each solution of the problem a column generation algorithm adds one
or several columns that improve the current solution. These columns must have a negative reduced
cost (in a minimization problem) and are calculated based on the dual value of the current solution.
For solving large MIP problems, column generation typically has to be combined with aBranch-and-Bound search, leading to a so-called Branch-and-Price algorithm. The example problem
described below is solved by solving a sequence of LPs without starting a tree search.

11.2.1

Example problem
A paper mill produces rolls of paper of a ﬁxed width MAXWIDTH that are subsequently cut into smallerrolls according to the customer orders. The rolls can be cut into NWIDTHS different sizes. The orders
are given as demands for each width i (DEMANDi ). The objective of the paper mill is to satisfy thedemand with the smallest possible number of paper rolls in order to minimize the losses.

11.2.2Model formulation
The objective of minimizing the total number of rolls can be expressed as choosing the best set of
cutting patterns for the current set of demands. Since it may not be obvious how to calculate all
possible cutting patterns by hand, we start off with a basic set of patterns (PATTERNS1 ,...,PATTERNSNWIDTH ), that consists of cutting small rolls all of the same width as many times as possible(and at most the demanded quantity) out of the large roll. This type of problem is called a cutting stock
problem.
If we deﬁne variables usej to denote the number of times a cutting pattern j(j ∈ WIDTHS = {1, ..., NWIDTH}) is used, then the objective becomes to minimize the sum of these

Fair Isaac Corporation Conﬁdential and Proprietary Information

89More about Integer Programming

variables, subject to the constraints that the demand for every size has to be met.
X
usej
minimizej∈WIDTHSXPATTERNSij · usej ≥ DEMANDij∈WIDTHS∀j ∈ WIDTHS : usej ≤ ceil(DEMANDj /PATTERNSjj ), usej ∈ IN
Function ceil means rounding to the next larger integer value.

11.2.3

Implementation
The ﬁrst part of the Mosel model paper.mos implementing this problem looks as follows:model Papermill
uses "mmxprs"
forward procedure column_gen
forward function knapsack(C:array(range) of real, A:array(range) of real,
B:real, D:array(range) of integer,
xbest:array(range) of integer,
pass: integer): real
forward procedure show_new_pat(dj:real, vx: array(range) of integer)
declarations
NWIDTHS = 5
WIDTHS = 1..NWIDTHS
RP: range
MAXWIDTH = 94
EPS = 1e-6

! Number of different widths! Range of widths! Range of cutting patterns! Maximum roll width! Zero tolerance

WIDTH: array(WIDTHS) of real
! Possible widths
DEMAND: array(WIDTHS) of integer! Demand per width
PATTERNS: array(WIDTHS,WIDTHS) of integer ! (Basic) cutting patterns
use: dynamic array(RP) of mpvar
soluse: dynamic array(RP) of real
Dem: array(WIDTHS) of linctr
MinRolls: linctr! Rolls per pattern! Solution values for variables `use'! Demand constraints! Objective function

KnapCtr, KnapObj: linctr
x: array(WIDTHS) of mpvar
end-declarations! Knapsack constraint+objective! Knapsack variables

WIDTH:: [ 17, 21, 22.5, 24, 29.5]
DEMAND:: [150, 96,
48, 108, 227]
forall(j in WIDTHS)! Make basic patterns
PATTERNS(j,j) := minlist(floor(MAXWIDTH/WIDTH(j)),DEMAND(j))
forall(j in WIDTHS) do
create(use(j))! Create NWIDTHS variables `use'
use(j) is_integer! Variables are integer and boundeduse(j) <= integer(ceil(DEMAND(j)/PATTERNS(j,j)))end-doMinRolls:= sum(j in WIDTHS) use(j)! Objective: minimize no. of rolls

forall(i in WIDTHS)! Satisfy all demands
Dem(i):= sum(j in WIDTHS) PATTERNS(i,j) ⁎ use(j) >= DEMAND(i)column_gen! Column generation at top node

minimize(MinRolls)! Compute the best integer solution

Fair Isaac Corporation Conﬁdential and Proprietary Information

90More about Integer Programming! for the current problem (including! the new columns)
writeln("Best integer solution: ", getobjval, " rolls")
write(" Rolls per pattern: ")forall(i in RP) write(getsol(use(i)),", ")The paper mill can satisfy the demand with just the basic set of cutting patterns, but it is likely to incur
signiﬁcant losses through wasting more than necessary of every large roll and by cutting more smallrolls than its customers have ordered. We therefore employ a column generation heuristic to ﬁnd moresuitable cutting patterns.
The following procedure column_gen deﬁnes a column generation loop that is executed at the top
node (this heuristic was suggested by M. Savelsbergh for solving a similar cutting stock problem). The
column generation loop performs the following steps:solve the LP and save the basis



get the solution values

compute a more proﬁtable cutting pattern based on the current solution

generate a new column (= cutting pattern): add a term to the objective function and to thecorresponding demand constraints
load the modiﬁed problem and load the saved basis

To be able to increase the number of variables usej in this function, these variables have been declaredat the beginning of the program as a dynamic array without specifying any index range.
By setting Mosel’s comparison tolerance to EPS, the test zbest = 0 checks whether zbest lies within
EPS of 0 (see explanation in Section 11.1).
Switching off presolve for the column generation problem generally helps to improve performance
when iteratively resolving the problem after adding a new column and warm-starting it with theprevious basis.
procedure column_gen
declarations
dualdem: array(WIDTHS) of real
xbest: array(WIDTHS) of integer
dw, zbest, objval: realbas: basis
end-declarations
setparam("XPRS_PRESOLVE", 0)
setparam("zerotol", EPS)
npatt:=NWIDTHS
npass:=1
while(true) do
minimize(XPRS_LIN, MinRolls)
savebasis(bas)objval:= getobjval! Switch presolve off! Set comparison tolerance of Mosel! Solve the LP! Save the current basis! Get the objective value! Get the solution values
forall(j in 1..npatt) soluse(j):=getsol(use(j))
forall(i in WIDTHS) dualdem(i):=getdual(Dem(i))! Solve a knapsack problem
zbest:= knapsack(dualdem, WIDTH, MAXWIDTH, DEMAND, xbest, npass) - 1.0
write("Pass ", npass, ": ")if zbest = 0 then

Fair Isaac Corporation Conﬁdential and Proprietary Information

91More about Integer Programming
writeln("no profitable column found.\n")
break
else
show_new_pat(zbest, xbest)! Print the new pattern
npatt+=1
create(use(npatt))! Create a new var. for this patternuse(npatt) is_integer
MinRolls+= use(npatt)! Add new var. to the objective
dw:=0
forall(i in WIDTHS)
if xbest(i) > 0 thenDem(i)+= xbest(i)⁎use(npatt)! Add new var. to demand constr.s
dw:= maxlist(dw, ceil(DEMAND(i)/xbest(i) ))end-ifuse(npatt) <= dw! Set upper bound on the new var.
loadprob(MinRolls)
loadbasis(bas)end-ifnpass+=1
end-do! Reload the problem! Load the saved basis

writeln("Solution after column generation: ", objval, " rolls, ",getsize(RP), " patterns")
write("
Rolls per pattern: ")forall(i in RP) write(soluse(i),", ")writelnsetparam("XPRS_PRESOLVE", 1)! Switch presolve on

end-procedure

The preceding procedure column_gen calls the following auxiliary function knapsack to solve an
integer knapsack problem of the formXmaximize z =Ci · xjj∈WIDTHSX

A j · xj ≤ B

j∈WIDTHS∀j ∈ WIDTHS : xj integer∀j ∈ WIDTHS : xj ≤ Dj
The function knapsack solves a second optimization problem that is independent of the main, cutting
stock problem since the two have no variables in common. We thus effectively work with two problems
in a single Mosel model.
For efﬁciency reasons we have deﬁned the knapsack variables and constraints globally. The integrality
condition on the knapsack variables remains unchanged between several calls to this function, so weestablish it when solving the ﬁrst knapsack problem. On the other hand, the knapsack constraint andthe objective function have different coefﬁcients at every execution, so we need to replace them every
time the function is called.
We reset the knapsack constraints to 0 at the end of this function so that they do not unnecessarily
increase the size of the main problem. The same is true in the other sense: hiding the demand
constraints while solving the knapsack problem makes life easier for the optimizer, but is not essential
for getting the correct solution.
function knapsack(C:array(range) of real, A:array(range) of real, B:real,
D:array(range) of integer, xbest:array(range) of integer,
pass: integer):real

Fair Isaac Corporation Conﬁdential and Proprietary Information

92More about Integer Programming! Hide the demand constraints
forall(j in WIDTHS) sethidden(Dem(j), true)! Define the knapsack problem
KnapCtr := sum(j in WIDTHS) A(j)⁎x(j) <= B
KnapObj := sum(j in WIDTHS) C(j)⁎x(j)! Integrality condition
if(pass=1) thenforall(j in WIDTHS) x(j) is_integer
forall(j in WIDTHS) x(j) <= D(j)end-ifmaximize(KnapObj)
returned:=getobjval
forall(j in WIDTHS) xbest(j):=round(getsol(x(j)))! Reset knapsack constraint and objective, unhide demand constraints
KnapCtr := 0
KnapObj := 0
forall(j in WIDTHS) sethidden(Dem(j), false)
end-function

To complete the model, we add the following procedure show_new_pat to print every new pattern weﬁnd.
procedure show_new_pat(dj:real, vx: array(range) of integer)
declarations
dw: real
end-declarations
writeln("new pattern found with marginal cost ", dj)
write("
Widths distribution: ")dw:=0
forall(i in WIDTHS) do
write(WIDTH(i), ":", vx(i), " ")dw += WIDTH(i)⁎vx(i)end-dowriteln("Total width: ", dw)
end-procedure
end-model

11.2.4

Alternative implementation: Working with multiple problems
The implementation of the function knapsack in the previous section uses the sethidden
functionality to blend out parts of the problem deﬁnition. The two parts of the problem (the main
cutting stock problem and the problem solved in the knapsack routine) do not have any elements incommon, that is, we really are solving two different problems within a single model.
With Mosel 3.0 it becomes possible to formulate this model as two separate problems within the samemodel ﬁle.
The implementation as two separate problems in the model ﬁle papers.mos requires only few
changes to the previous model formulation:
1. The declaration of a subproblem ’Knapsack’ is added to the global declarations at the start of themodel deﬁnition.
declarations
Knapsack: mpproblem
end-declarations! Knapsack subproblem

2. The implementation of function knapsack now works within the subproblem ’Knapsack’ instead

Fair Isaac Corporation Conﬁdential and Proprietary Information

93More about Integer Programmingof hiding and unhiding subsets of the constraints. The scope of the subproblem is marked by the
keywords with mpproblem [do ... end-do].
function knapsack(C:array(range) of real, A:array(range) of real, B:real,
D:array(range) of integer, xbest:array(range) of integer,
pass: integer):real
with Knapsack do! Redefine the knapsack problem
KnapCtr := sum(j in WIDTHS) A(j)⁎x(j) <= B
KnapObj := sum(j in WIDTHS) C(j)⁎x(j)! Integrality condition
if pass=1 thenforall(j in WIDTHS) x(j) is_integer
forall(j in WIDTHS) x(j) <= D(j)end-ifmaximize(KnapObj)
returned:=getobjval
forall(j in WIDTHS) xbest(j):=round(getsol(x(j)))
end-doend-function

Fair Isaac Corporation Conﬁdential and Proprietary Information

94

CHAPTER 12

Extensions to Linear Programming
The two examples (recursion and Goal Programming) in this chapter show how Mosel can be used to
implement extensions of Linear Programming.

12.1

Recursion
Recursion, more properly known as Successive Linear Programming, is a technique whereby LP may be
used to solve certain non-linear problems. Some coefﬁcients in an LP problem are deﬁned to befunctions of the optimal values of LP variables. When an LP problem has been solved, the coefﬁcientsare re-evaluated and the LP re-solved. Under some assumptions this process may converge to a local(though not necessarily a global) optimum.

12.1.1

Example problem
Consider the following ﬁnancial planning problem: We wish to determine the yearly interest rate x sothat for a given set of payments we obtain the ﬁnal balance of 0. Interest is paid quarterly according to
the following formula:interestt = (92/365) · balancet · interestr ate
The balance at time t (t = 1, ..., T) results from the balance of the previous period t – 1 and the net ofpayments and interest:
nett = Paymentst – interesttbalancet = balancet–1 – nett

12.1.2Model formulation
This problem cannot be modeled just by LP because we have the T productsbalancet · interestr ate
which are non-linear. To express an approximation of the original problem by LP we replace the interest
rate variable x by a (constant) guess X of its value and a deviation variable dx
x = X + dx
The formula for the quarterly interest payment it therefore becomesinterestt=

92/365 · (balancet–1 · x)=

92/365 · (balancet–1 · (X + dx))=

92/365 · (balancet–1 · X + balancet–1 · dx)

Fair Isaac Corporation Conﬁdential and Proprietary Information95

Extensions to Linear Programmingwhere balancet is the balance at the beginning of period t.
We now also replace the balance balancet–1 in the product with dx by a guess Bt–1 and a deviation dbt–1
iinterestt=

92/365 · (balancet–1 · X + (Bt–1 + dbt–1 ) · dx)=

92/365 · (balancet–1 · X + Bt–1 · dx + dbt–1 · dx)

which can be approximated by dropping the product of the deviation variables
interestt=

92/365 · (balancet–1 · X + Bt–1 · dx)

To ensure feasibility we add penalty variables eplust and eminust for positive and negative deviations in
the formulation of the constraint:interestt=

92/365 · (balancet–1 · X + Bt–1 · dx + eplust – eminust )The objective of the problem is to get feasible, that is to minimize the deviations:Xminimize(eplust + eminust )t∈QUARTERS

12.1.3

Implementation
The Mosel model (ﬁle recurse.mos) then looks as follows (note the balance variables balancet aswell as the deviation dx and the quarterly nets nett are deﬁned as free variables, that is, they may takeany values between minus and plus inﬁnity):model Recurseuses "mmxprs"
forward procedure solve_recurse
declarations
T=6
QUARTERS=1..T
P,R,V: array(QUARTERS) of real
B: array(QUARTERS) of real
X: real! Time horizon! Range of time periods! Payments! Initial guess as to balances b(t)! Initial guess as to interest rate x

interest: array(QUARTERS) of mpvar ! Interest
net: array(QUARTERS) of mpvar! Net
balance: array(QUARTERS) of mpvar ! Balance
x: mpvar! Interest rate
dx: mpvar! Change to x
eplus, eminus: array(QUARTERS) of mpvar ! + and - deviations
end-declarations
X:= 0.00
B:: [1, 1, 1, 1, 1, 1]
P:: [-1000, 0, 0, 0, 0, 0]
R:: [206.6, 206.6, 206.6, 206.6, 206.6, 0]
V:: [-2.95, 0, 0, 0, 0, 0]! net = payments - interest
forall(t in QUARTERS) net(t) = (P(t)+R(t)+V(t)) - interest(t)! Money balance across periods
forall(t in QUARTERS) balance(t) = if(t>1, balance(t-1), 0) - net(t)forall(t in 2..T) Interest(t):=! Approximation of interest-(365/92)⁎interest(t) + X⁎balance(t-1) + B(t-1)⁎dx + eplus(t) - eminus(t) = 0

Fair Isaac Corporation Conﬁdential and Proprietary Information96Extensions to Linear Programming
Def:= X + dx = x! Define the interest rate: x = X + dx

Feas:= sum(t in QUARTERS) (eplus(t)+eminus(t))! Objective: get feasible

interest(1) = 0! Initial interest is zero
forall (t in QUARTERS) net(t) is_free
forall (t in 1..T-1) balance(t) is_freebalance(T) = 0! Final balance is zerodx is_free
minimize(Feas)! Solve the LP-problem

solve_recurse! Recursion loop

! Print the solution
writeln("\nThe interest rate is ", getsol(x))
write(strfmt("t",5), strfmt(" ",4))
forall(t in QUARTERS) write(strfmt(t,5), strfmt(" ",3))write("\nBalances ")forall(t in QUARTERS) write(strfmt(getsol(balance(t)),8,2))
write("\nInterest ")forall(t in QUARTERS) write(strfmt(getsol(interest(t)),8,2))
end-model

In the model above we have declared the procedure solve_recurse that executes the recursion but it
has not yet been deﬁned. The recursion on x and the balancet (t = 1, ..., T – 1) is implemented by thefollowing steps:(a) The Bt–1 in constraints Interestt get the prior solution value of balancet–1(b) The X in constraints Interestt get the prior solution value of x(c) The X in constraint Def gets the prior solution value of x
We say we have converged when the change in dx (variation) is less than 0.000001 (TOLERANCE). By
setting Mosel’s comparison tolerance to this value the test variation > 0 checks whether variation isgreater than TOLERANCE.
procedure solve_recurse
declarations
TOLERANCE=0.000001
variation: real
BC: array(QUARTERS) of realbas: basis
end-declarations
setparam("zerotol", TOLERANCE)
variation:=1.0ct:=0! Convergence tolerance! Variation of x
! LP basis! Set Mosel comparison tolerance

while(variation>0) do
savebasis(bas)! Save the current basis
ct+=1
forall(t in 2..T)BC(t-1):= getsol(balance(t-1))! Get solution values for balance(t)'s
XC:= getsol(x)! and xwrite("Round ", ct, " x:", getsol(x), " (variation:", variation,"), ")writeln("Simplex iterations: ", getparam("XPRS_SIMPLEXITER"))
forall(t in 2..T) do! Update coefficientsInterest(t)+= (BC(t-1)-B(t-1))⁎dx
B(t-1):=BC(t-1)
Interest(t)+= (XC-X)⁎balance(t-1)
end-doDef+= XC-X
X:=XC
oldxval:=XC! Store solution value of x

Fair Isaac Corporation Conﬁdential and Proprietary Information

97

Extensions to Linear Programming
loadprob(Feas)loadbasis(bas)minimize(Feas)! Reload the problem into the optimizer! Reload previous basis! Re-solve the LP-problem

variation:= abs(getsol(x)-oldxval) ! Change in dx
end-doend-procedure

With the initial guesses 0 for X and 1 for all Bt the model converges to an interest rate of 5.94413% (x =0.0594413).

12.2

Goal Programming
Goal Programming is an extension of Linear Programming in which targets are speciﬁed for a set of
constraints. In Goal Programming there are two basic models: the pre-emptive (lexicographic) modeland the Archimedian model. In the pre-emptive model, goals are ordered according to priorities. The
goals at a certain priority level are considered to be inﬁnitely more important than the goals at the nextlevel. With the Archimedian model weights or penalties for not achieving targets must be speciﬁed, andwe attempt to minimize the sum of the weighted infeasibilities.
If constraints are used to construct the goals, then the goals are to minimize the violation of theconstraints. The goals are met when the constraints are satisﬁed.
The example in this section demonstrates how Mosel can be used for implementing pre-emptive Goal
Programming with constraints. We try to meet as many goals as possible, taking them in priority order.

12.2.1

Example problem
The objective is to solve a problem with two variables x and y (x, y ≥ 0), the constraint
100 · x + 60 · y ≤ 600
and the three goal constraints
Goal1 : 7 · x + 3 · y ≥ 40
Goal2 : 10 · x + 5 · y = 60
Goal3 : 5 · x + 4 · y ≥ 35
where the order given corresponds to their priorities.

12.2.2

Implementation
To increase readability, the implementation of the Mosel model (ﬁle goalctr.mos) is organized into
three blocks: the problem is stated in the main part, procedure preemptive implements the solution
strategy via preemptive Goal Programming, and procedure print_sol produces a nice solution
printout.
model GoalCtr
uses "mmxprs"
forward procedure preemptive
forward procedure print_sol(i:integer)
declarations
NGOALS=3x,y: mpvar
dev: array(1..2⁎NGOALS) of mpvar

Fair Isaac Corporation Conﬁdential and Proprietary Information! Number of goals! Decision variables! Deviation from goals

98Extensions to Linear Programming
MinDev: linctr
Goal: array(1..NGOALS) of linctr
end-declarations! Objective function! Goal constraints

100⁎x + 60⁎y <= 600! Define a constraint! Define the goal constraints
Goal(1):= 7⁎x + 3⁎y >= 40
Goal(2):= 10⁎x + 5⁎y = 60
Goal(3):= 5⁎x + 4⁎y >= 35
preemptive! Pre-emptive Goal Programming

At the end of the main part, we call procedure preemptive to solve this problem by pre-emptive Goal
Programming. In this procedure, the goals are at ﬁrst entirely removed from the problem (‘hidden’). We
then add them successively to the problem and re-solve it until the problem becomes infeasible, that is,the deviation variables forming the objective function are not all 0. Depending on the constraint type(obtained with gettype) of the goals, we need one (for inequalities) or two (for equalities) deviation
variables.
Let us have a closer look at the ﬁrst goal (Goal1 ), a ≥ constraint: the left hand side (all terms withdecision variables) must be at least 40 to satisfy the constraint. To ensure this, we add a dev2 . The goal
constraint becomes 7 · x + 3 · y + dev2 ≥ 40 and the objective function is to minimize dev2 . If this isfeasible (0-valued objective), then we remove the deviation variable from the goal, thus turning it into a
hard constraint. It is not required to remove it from the objective since minimization will always force
this variable to take the value 0.
We then continue with Goal2 : since this is an equation, we need variables for positive and negative
deviations, dev3 and dev4 . We add dev4 – dev3 to the constraint, turning it into
10 · x + 5 · y + dev4 – dev3 = 60 and the objective now is to minimize the absolute deviation dev4 + dev3 .
And so on.
procedure preemptive! Remove (=hide) goal constraint from the problem
forall(i in 1..NGOALS) sethidden(Goal(i), true)
i:=0
while (i<NGOALS) do
i+=1
sethidden(Goal(i), false)! Add (=unhide) the next goalcase gettype(Goal(i)) of! Add deviation variable(s)CT_GEQ: do
Deviation:= dev(2⁎i)
MinDev += Deviation
end-doCT_LEQ: do
Deviation:= -dev(2⁎i-1)
MinDev += dev(2⁎i-1)
end-doCT_EQ : do
Deviation:= dev(2⁎i) - dev(2⁎i-1)
MinDev += dev(2⁎i) + dev(2⁎i-1)
end-doelse
writeln("Wrong constraint type")
break
end-case
Goal(i)+= Deviation
minimize(MinDev)! Solve the LP-problem
writeln(" Solution(", i,"): x: ", getsol(x), ", y: ", getsol(y))
if getobjval>0 thenwriteln("Cannot satisfy goal ",i)Fair Isaac Corporation Conﬁdential and Proprietary Information99

Extensions to Linear Programmingbreak
end-ifGoal(i)-= Deviation
end-doprint_sol(i)end-procedure! Remove deviation variable(s) from goal
! Solution printout

The procedure sethidden(c:linctr, b:boolean) has already been used in the previous chapter(Section 11.2) without giving any further explanation. With this procedure, constraints can be removed(‘hidden’) from the problem solved by the optimizer without deleting them in the problem deﬁnition. So
effectively, the optimizer solves a subproblem of the problem originally stated in Mosel.
To complete the model, below is the procedure print_sol for printing the results.
procedure print_sol(i:integer)
declarations
STypes={CT_GEQ, CT_LEQ, CT_EQ}
ATypes: array(STypes) of string
end-declarations
ATypes::([CT_GEQ, CT_LEQ, CT_EQ])[">=", "<=", "="]
writeln(" Goal", strfmt("Target",11), strfmt("Value",7))
forall(g in 1..i)writeln(strfmt(g,4), strfmt(ATypes(gettype(Goal(g))),4),
strfmt(-getcoeff(Goal(g)),6),
strfmt( getact(Goal(g))-getsol(dev(2⁎g))+getsol(dev(2⁎g-1)) ,8))
forall(g in 1..NGOALS)
if (getsol(dev(2⁎g))>0) thenwriteln(" Goal(",g,") deviation from target: -", getsol(dev(2⁎g)))elif (getsol(dev(2⁎g-1))>0) thenwriteln(" Goal(",g,") deviation from target: +", getsol(dev(2⁎g-1)))
end-ifend-procedure
end-model

When running the program, one ﬁnds that the ﬁrst two goals can be satisﬁed, but not the third.

Fair Isaac Corporation Conﬁdential and Proprietary Information

100III. Working with the Mosel libraries

Overview
Whilst the two previous parts have shown how to work with the Mosel Language, this part introduces
the programming language interface of Mosel in the form of the Mosel C libraries. The C interface isprovided in the form of two libraries; it may especially be of interest to users whowant to integrate models and/or solution algorithms written with Mosel into some larger systemwant to (re)use already existing parts of algorithms written in Cwant to interface Mosel with other software, for instance for graphically displaying results.

Other programming language interfaces available for Mosel are its Java, .NET, C# and VBA interfaces.
They will be introduced with the help of small examples in Chapter 14.
All these programming language interfaces only enable the user to access models, but not to modify
them. The latter is only possible with the Mosel Native Interface. Even more importantly, the Native
Interface makes it possible to add new constants, types, and subroutines to the Mosel Language. For
more detail the reader is referred to the Native Interface user guide that is provided as a separate
document. The Mosel Native Interface requires an additional licence.

Fair Isaac Corporation Conﬁdential and Proprietary Information

102

CHAPTER 13

C interface
This chapter gives an introduction to the C interface of Mosel. It shows how to execute models from C
and how to access modeling objects from C. It is not possible to make changes to Mosel modelingobjects from C using this interface, but the data and parameters used by a model may be modiﬁed via
ﬁles or run time parameters.

13.1

Basic tasks
To work with a Mosel model, in the C language or with the command line interpreter, it always needs tobe compiled, then loaded into Mosel and executed. In this section we show how to perform these basic
tasks in C.

13.1.1

Compiling a model in C
The following example program shows how Mosel is initialized in C, and how a model ﬁle (extension.mos) is compiled into a binary model (BIM) ﬁle (extension .bim). To use the Mosel Model Compiler
Library, we need to include the header ﬁle xprm_mc.h at the start of the C program.
For the sake of readability, in this program (ﬁle ugcomp.c), as for all others in this chapter, we only
implement a rudimentary testing for errors.
#include <stdlib.h>#include "xprm_mc.h"
int main(){if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if(XPRMcompmod(NULL, "burglar2.mos", NULL, "Knapsack example"))return 2;/⁎ Compile the model burglar2.mos,
output the file burglar2.bim ⁎/XPRMfinish();/⁎ Finish Mosel, clear everything ⁎/return 0;}

The model burglar2.mos used here is the same as model burglari.mos in Section 2.1.3, but
reading the data from ﬁle.
With version 1.4 of Mosel it becomes possible to redirect the BIM ﬁle that is generated by thecompilation. Instead of writing it out to a physical ﬁle it may, for instance, be kept in memory or be
written out in compressed format. The interested reader is refered to the whitepaper Generalized ﬁlehandling in Mosel.

Fair Isaac Corporation Conﬁdential and Proprietary Information

103

C interface

13.1.2

Executing a model in C
The example in this section shows how a Mosel binary model ﬁle (BIM) can be executed in C. The BIM
ﬁle can of course be generated within the same program where it is executed, but here we leave out thisstep. A BIM ﬁle is an executable version of a model, but it does not include any data that is read in by
the model from external ﬁles. It is portable, that is, it may be executed on a different type ofarchitecture than the one it has been generated on. A BIM ﬁle produced by the Mosel compiler ﬁrst
needs to be loaded into Mosel (function XPRMloadmod) and can then be run by a call to function
XPRMrunmod. To use these functions, we need to include the header ﬁle xprm_rt.h at the beginningof our program (named ugrun.c).
#include <stdio.h>#include "xprm_rt.h"
int main(){
XPRMmodel mod;int result;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if((mod=XPRMloadmod("burglar2.bim", NULL))==NULL)
return 2;/⁎ Load a BIM file ⁎/if(XPRMrunmod(mod,&result,NULL))
return 3;/⁎ Run the model ⁎/XPRMfinish();/⁎ Finish Mosel, clear everything ⁎/return 0;}

The compile/load/run sequence may also be performed with a single function call to XPRMexecmod (inthis case we need to include the header ﬁle xprm_mc.h):#include <stdio.h>#include "xprm_mc.h"
int main(){
int result;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/XPRMfinish();/⁎ Finish Mosel, clear everything ⁎//⁎ Execute = compile/load/run a model ⁎/if(XPRMexecmod(NULL, "burglar2.mos", NULL, &result, NULL))
return 2;

return 0;}

13.1.3

Termination
All program examples in this manual only serve to execute Mosel models. The corresponding modeland Mosel itself are terminated (unloaded from memory) with the end of the C program. However, forembedding the execution of a Mosel model into some larger application it may be desirable to free thespace used by the model or the execution of Mosel before the end of the application program. To thisaim Mosel provides the functions XPRMresetmod, XPRMunloadmod, and XPRMfinish.
The function XPRMresetmod frees some resources allocated to a model, in particular (solution) data

Fair Isaac Corporation Conﬁdential and Proprietary Information

104

C interface

held in memory or temporary ﬁles that may have been created during its execution. The model remains
loaded for later re-use. With a call to XPRMunloadmod a model is unloaded and all related resources
are freed.
Function XPRMfinish performs the unloading of all models, frees all memory used by Mosel, and also
removes the temporary directory/ﬁles that have been created by Mosel.

13.2

Parameters
In Part I the concept of parameters in Mosel has been introduced: when a Mosel model is executedfrom the command line, it is possible to pass new values for its parameters into the model. The sameis possible with a model run in C. If, for instance, we want to run model ‘Prime’ from Section 8.3 toobtain all prime numbers up to 500 (instead of the default value 100 set for the parameter LIMIT in themodel), we may start a program with the following lines:
XPRMmodel mod;int result;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if((mod=XPRMloadmod("prime.bim",NULL))==NULL)
return 2;/⁎ Load a BIM file ⁎/if(XPRMrunmod(mod,&result,"LIMIT=500"))
return 3;/⁎ Run the model ⁎/To use function XPRMexecmod instead of the compile/load/run sequence we have:
int result;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎//⁎ Execute with new parameter settings ⁎/if(XPRMexecmod(NULL,"prime.mos","LIMIT=500",&result,NULL))
return 2;

13.3

Accessing modeling objects and solution values
Using the Mosel libraries, it is not only possible to compile and run models, but also to accessinformation on the different modeling objects.

13.3.1

Accessing sets
A complete version of a program (ﬁle ugparam1.c) for running the model ‘Prime’ mentioned in theprevious section may look as follows (we work with a model prime2 that corresponds to the one
printed in Section 8.3 but with all output printing removed because we are doing this in C, furthermore
all entities accessed from C must be explicitly declared as public):#include <stdio.h>#include "xprm_mc.h"
int main(){
XPRMmodel mod;
XPRMalltypes rvalue, setitem;

Fair Isaac Corporation Conﬁdential and Proprietary Information

105

C interface
XPRMset set;
int result, type, i, size, first, last;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if(XPRMexecmod(NULL, "prime2.mos", "LIMIT=500", &result, &mod))return 2;/⁎ Execute the model ⁎/type=XPRMfindident(mod, "SPrime", &rvalue); /⁎ Get the object 'SPrime' ⁎/if((XPRM_TYP(type)!=XPRM_TYP_INT)||/⁎ Check the type: ⁎/(XPRM_STR(type)!=XPRM_STR_SET))/⁎ it must be a set of integers ⁎/return 3;
set = rvalue.set;
size = XPRMgetsetsize(set);/⁎ Get the size of the set ⁎/if(size>0){
first = XPRMgetfirstsetndx(set);/⁎ Get number of the first index ⁎/last = XPRMgetlastsetndx(set);/⁎ Get number of the last index ⁎/printf("Prime numbers from 2 to %d:\n", LIM);
for(i=first;i<=last;i++)/⁎ Print all set elements ⁎/printf(" %d,", XPRMgetelsetval(set,i,&setitem)->integer);
printf("\n");}XPRMfinish();/⁎ Finish Mosel ⁎/return 0;}

To print the contents of set SPrime that contains the desired result (prime numbers between 2 and500), we ﬁrst retrieve the Mosel reference to this object using function XPRMfindident. We are thenable to enumerate the elements of the set (using functions XPRMgetfirstsetndx andXPRMgetlastsetndx) and obtain their respective values with XPRMgetelsetval.

13.3.2Retrieving solution values
The following program ugsol1.c executes the model ‘Burglar3’ (the same as model ‘Burglar2’ from
Chapter 2 but with all output printing removed and all model entities declared as public) and prints
out its solution.
#include <stdio.h>#include "xprm_rt.h"
int main(){
XPRMmodel mod;
XPRMalltypes rvalue, itemname;
XPRMarray varr, darr;
XPRMmpvar x;
XPRMset set;
int indices[1], result, type;
double val;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if((mod=XPRMloadmod("burglar3.bim", NULL))==NULL)
return 2;
if(XPRMrunmod(mod, &result, NULL))
return 3;/⁎ Load a BIM file ⁎//⁎ Run the model (includes
optimization) ⁎/if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)

Fair Isaac Corporation Conﬁdential and Proprietary Information

106

C interface
return 4;/⁎ Test whether a solution is found ⁎/printf("Objective value: %g\n", XPRMgetobjval(mod));/⁎ Print the obj. function value ⁎/type=XPRMfindident(mod,"take",&rvalue);if((XPRM_TYP(type)!=XPRM_TYP_MPVAR)||(XPRM_STR(type)!=XPRM_STR_ARR))
return 5;
varr = rvalue.array;/⁎ Get the model object 'take' ⁎//⁎ Check the type: ⁎//⁎ it must be an `mpvar' array ⁎/type=XPRMfindident(mod,"VALUE",&rvalue);if((XPRM_TYP(type)!=XPRM_TYP_REAL)||(XPRM_STR(type)!=XPRM_STR_ARR))
return 6;
darr = rvalue.array;/⁎ Get the model object 'VALUE' ⁎//⁎ Check the type: ⁎//⁎ it must be an array of reals ⁎/type=XPRMfindident(mod,"ITEMS",&rvalue);if((XPRM_TYP(type)!=XPRM_TYP_STRING)||(XPRM_STR(type)!=XPRM_STR_SET))
return 7;
set = rvalue.set;/⁎ Get the model object 'ITEMS' ⁎//⁎ Check the type: ⁎//⁎ it must be a set of strings ⁎/XPRMgetfirstarrentry(varr, indices);/⁎ Get the first entry of array varr
(we know that the array is denseand has a single dimension) ⁎/do{
XPRMgetarrval(varr, indices, &x);/⁎ Get a variable from varr ⁎/XPRMgetarrval(darr, indices, &val);/⁎ Get the corresponding value ⁎/printf("take(%s): %g\t (item value: %g)\n", XPRMgetelsetval(set, indices[0],&itemname)->string, XPRMgetvsol(mod,x), val);/⁎ Print the solution value ⁎/} while(!XPRMgetnextarrentry(varr, indices)); /⁎ Get the next index tuple ⁎/XPRMfinish();/⁎ Finish Mosel, clear everything ⁎/return 0;}

The array of variables varr is enumerated using the array functions XPRMgetfirstarrentry and
XPRMgetnextarrentry. These functions may be applied to arrays of any type and dimension (forhigher numbers of dimensions, merely the size of the array indices that is used to store theindex-tuples has to be adapted). With these functions we run systematically through all possiblecombinations of index tuples, hence the hint at dense arrays in the example. In the case of sparsearrays it is preferrable to use different enumeration functions that only enumerate those entries that aredeﬁned (see next section).

13.3.3Sparse arrays
In Chapter 3 the problem ‘Transport’ has been introduced. The objective of this problem is to calculate
the ﬂows ﬂowpr from a set of plants to a set of sales regions that satisfy all demand and supply
constraints and minimize the total cost. Not all plants may deliver goods to all regions. The ﬂow
variables ﬂowpr are therefore deﬁned as a sparse array. The following example (ﬁle ugarray1.c)
prints out all existing entries of the array of variables.
#include <stdio.h>#include <stdlib.h>#include "xprm_rt.h"
int main(){
XPRMmodel mod;
XPRMalltypes rvalue;

Fair Isaac Corporation Conﬁdential and Proprietary Information107

C interface
XPRMarray varr;
XPRMset ⁎sets;int ⁎indices, dim, result, type, i;if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if((mod=XPRMloadmod("transport.bim", NULL))==NULL)
return 2;
if(XPRMrunmod(mod, &result, NULL))
return 3;/⁎ Load a BIM file ⁎//⁎ Run the model ⁎/type=XPRMfindident(mod,"flow",&rvalue); /⁎ Get the model object named 'flow' ⁎/if((XPRM_TYP(type)!=XPRM_TYP_MPVAR)||/⁎ Check the type: ⁎/(XPRM_STR(type)!=XPRM_STR_ARR))/⁎ it must be an array of unknowns ⁎/return 4;
varr=rvalue.array;dim = XPRMgetarrdim(varr);/⁎ Get the number of dimensions of
the array ⁎/indices = (int ⁎)malloc(dim⁎sizeof(int));
sets = (XPRMset ⁎)malloc(dim⁎sizeof(XPRMset));XPRMgetarrsets(varr,sets);/⁎ Get the indexing sets ⁎/XPRMgetfirstarrtruentry(varr,indices); /⁎ Get the first true index tuple ⁎/do{printf("flow(");
for(i=0;i<dim-1;i++)printf("%s,",XPRMgetelsetval(sets[i],indices[i],&rvalue)->string);
printf("%s), ",XPRMgetelsetval(sets[dim-1],indices[dim-1],&rvalue)->string);} while(!XPRMgetnextarrtruentry(varr,indices)); /⁎ Get next true index tuple⁎/printf("\n");free(sets);
free(indices);
XPRMresetmod(mod);return 0;}

In this example, we ﬁrst get the number of indices (dimensions) of the array of variables varr (using
function XPRMgetarrdim). We use this information to allocate space for the arrays sets andindices that will be used to store the indexing sets and single index tuples for this array respectively.
We then read the indexing sets of the array (function XPRMgetarrsets) to be able to produce a niceprintout.
The enumeration starts with the ﬁrst deﬁned index tuple, obtained with function
XPRMgetfirstarrtruentry, and continues with a series of calls to XPRMgetnextarrtruentry
until all deﬁned entries have been enumerated.
At the end of the program example we have reset the model (using function XPRMresetmod), thus
freeing some resources allocated to it, in particular deleting temporary ﬁles that may have been createdduring its execution.

13.4

Exchanging data between an application and a model
In the previous sections we have seen how to obtain solution information and other data from a Mosel
model after its execution. For the integration of a model into an application a ﬂow of information in the
opposite sense, that is, from the host application to the model, will often also be required, in particular
if data are generated by the application that serve as input to the model. It is possible to write out thisdata to a (text) ﬁle or a database and read this ﬁle in from the model, but it is clearly more efﬁcient toFair Isaac Corporation Conﬁdential and Proprietary Information108

C interface

communicate such data in memory directly from the application to the model.
In this section we show two versions of our Burglar example where all input data is loaded from theapplication into the model, using dense and sparse data format respectively. The same communication
mechanism, namely a combination of the two I/O drivers (see Section 17.1 for further detail) raw andmem, is also used to write back the solution from the model to the calling application.
An alternative communication mechanism is presented in Section 13.4.3. Instead of working withblocks of predeﬁned size as in the previous cases, here data is passed through ﬂows, allowing for
dynamic sizing on the application level, a feature that is particularly useful for solution output withsparse data structures.
A separate example (Section 13.4.4) shows how to input and output scalar data.

13.4.1

Dense arrays
In the ﬁrst instance we are going to consider a version of the ‘Burglar’ model that corresponds to thevery ﬁrst version we have seen in Section 2.1 where all arrays are indexed by the range set ITEMS =1..8. In our C program ugiodense.c below, this corresponds to storing data in standard C arraysthat are communicated to the Mosel model at the start of its execution.
#include <stdio.h>#include "xprm_mc.h"double vdata[8]={15,100,90,60,40,15,10, 1};/⁎ Input data: VALUE ⁎/double wdata[8]={ 2, 20,20,30,40,30,60,10};/⁎ Input data: WEIGHT ⁎/double solution[8];/⁎ Array for solution values ⁎/int main(){
XPRMmodel mod;int i,result;char vdata_name[40];char wdata_name[40];char solution_name[40];char params[144];
if(XPRMinit())
return 1;/⁎ File name of input data 'vdata' ⁎//⁎ File name of input data 'wdata' ⁎//⁎ File name of solution values ⁎//⁎ Parameter string for model execution ⁎//⁎ Initialize Mosel ⁎//⁎ Prepare file names for 'initializations' using the 'raw' driver ⁎/sprintf(vdata_name, "noindex,mem:%p/%d", vdata, (int)sizeof(vdata));sprintf(wdata_name, "noindex,mem:%p/%d", wdata, (int)sizeof(wdata));sprintf(solution_name, "noindex,mem:%p/%d", solution, (int)sizeof(solution));/⁎ Pass file names as execution param.s ⁎/sprintf(params, "VDATA='%s',WDATA='%s',SOL='%s'", vdata_name, wdata_name,solution_name);if(XPRMexecmod(NULL, "burglar6.mos", params, &result, &mod))return 2;/⁎ Execute a model file ⁎/if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
return 3;/⁎ Test whether a solution is found ⁎//⁎ Display solution values obtained from the model ⁎/printf("Objective value: %g\n", XPRMgetobjval(mod));for(i=0;i<8;i++)printf(" take(%d): %g\n", i+1, solution[i]);XPRMresetmod(mod);/⁎ Reset the model ⁎/return 0;}

Fair Isaac Corporation Conﬁdential and Proprietary Information109

C interface

In this example we use the raw I/O driver for communication between the application and the model it
executes. Employing this driver means that data is saved in binary format. File names used with theraw driver have the form rawoption[,...],filename. The option noindex for this driver
indicates that data is to be stored in dense format, that is, just the data entries without any information
about the indices—this format supposes that the index set(s) is known in the Mosel model before data
is read in. The filename uses the mem driver, this means that data is stored in memory. The actual
location of the data is speciﬁed by giving the address of the corresponding memory block and its size.
The program above works with the following version of the ‘Burglar’ model where the locations of input
and output data are speciﬁed by the calling application through model parameters. Instead of printing
out the solution in the model, we copy the solution values of the decision variables take into the arrayof reals soltake that is written to memory and will be processed by the host application.
model Burglar6
uses "mmxprs"
parameters
VDATA = ''; WDATA = ''SOL = ''WTMAX = 102
end-parameters! Locations of input data
! Location for solution data output! Maximum weight allowed

declarations
ITEMS = 1..8

! Index range for items

VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of real

! Value of items! Weight of items

take: array(ITEMS) of mpvar
soltake: array(ITEMS) of real
end-declarations! 1 if we take item i; 0 otherwise! Solution values

initializations from 'raw:'
VALUE as VDATA WEIGHT as WDATA
end-initializations! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
maximize(MaxVal)! Solve the MIP-problem! Output solution to calling application
forall(i in ITEMS) soltake(i):= getsol(take(i))
initializations to 'raw:'
soltake as SOL
end-initializations
end-model

13.4.2Sparse arrays
Let us now take a look at the case where we use a set of strings instead of a simple range set to index
the various arrays in our model. Storing the indices with the data values makes necessary slightly more
complicated structures in our C program for the input and solution data. In the C program below (ﬁleugiosparse.c), every input data entry deﬁnes both, the value and the weight coefﬁcient for thecorresponding index.

Fair Isaac Corporation Conﬁdential and Proprietary Information110

C interface#include <stdio.h>#include "xprm_mc.h"
const struct{/⁎ Initial values for array 'data': ⁎/const char ⁎ind;/⁎index name ⁎/double val,wght;/⁎
value and weight data entries ⁎/} data[]={{"camera",15,2}, {"necklace",100,20}, {"vase",90,20},{"picture",60,30}, {"tv",40,40}, {"video",15,30},{"chest",10,60}, {"brick",1,10}};
const struct{const char ⁎ind;double val;
} solution[8];int main(){
XPRMmodel mod;int i,result;char data_name[40];char solution_name[40];char params[96];if(XPRMinit())
return 1;/⁎ Array to receive solution values: ⁎//⁎index name ⁎//⁎solution value ⁎//⁎ File name of input data 'data' ⁎//⁎ File name of solution values ⁎//⁎ Parameter string for model execution ⁎//⁎ Initialize Mosel ⁎//⁎ Prepare file names for 'initializations' using the 'raw' driver ⁎/sprintf(data_name, "slength=0,mem:%p/%d", data, (int)sizeof(data));sprintf(solution_name, "slength=0,mem:%p/%d", solution, (int)sizeof(solution));/⁎ Pass file names as execution param.s ⁎/sprintf(params, "DATA='%s',SOL='%s'", data_name, solution_name);if(XPRMexecmod(NULL, "burglar7.mos", params, &result, &mod))return 2;/⁎ Execute a model file ⁎/if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
return 3;/⁎ Test whether a solution is found ⁎//⁎ Display solution values obtained from the model ⁎/printf("Objective value: %g\n", XPRMgetobjval(mod));for(i=0;i<8;i++)printf(" take(%s): %g\n", solution[i].ind, solution[i].val);
XPRMresetmod(mod);return 0;}

The use of the two I/O drivers is quite similar to what we have seen before. We now pass on data insparse format, this means that every data entry is saved together with its index (tuple). Option
slength=0 of the raw driver indicates that strings are represented by pointers to null terminatedarrays of characters (C-string) instead of ﬁxed size arrays.
Similarly to the model of the previous section, the model burglar7.mos executed by the C program
above reads and writes data from/to memory using the raw driver and the locations are speciﬁed by
the calling application through the model parameters. Since the contents of the index set ITEMS is not
deﬁned in the model we have moved the declaration of the decision variables after the data input where
the contents of the set is known, thus avoiding the creation of the array of decision variables as adynamic array.
model Burglar7
uses "mmxprs"

Fair Isaac Corporation Conﬁdential and Proprietary Information111

C interface
parameters
DATA = ''SOL = ''WTMAX = 102
end-parameters! Location of input data
! Location for solution data output! Maximum weight allowed

declarations
ITEMS: set of string
VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of real
end-declarations! Index set for items! Value of items! Weight of items

initializations from 'raw:'[VALUE,WEIGHT] as DATA
end-initializations
declarations
take: array(ITEMS) of mpvar
end-declarations! 1 if we take item i; 0 otherwise! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
maximize(MaxVal)! Solve the MIP-problem! Output solution to calling application
forall(i in ITEMS) soltake(i):= getsol(take(i))
initializations to 'raw:'
soltake as SOL
end-initializations
end-model

13.4.3

Dynamic data
The two examples of in-memory communication of dense and sparse data in the preceding sections
have in commun that all data structures in the application, and in particular the structures to receive
output data, are of ﬁxed size. We therefore now introduce an alternative communication mechanism
working with ﬂows, that enables dynamic sizing of data structures on the application level, a featurethat is particularly useful for solution output where effective data sizes are not known a priori. This
communication mechanism is based on the callback I/O driver cb (see also Section 13.5). The main
body of our C program now looks as follows.
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "xprm_mc.h"/⁎ Input values for data: ⁎/char ⁎ind[]={"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"};/⁎ Index names ⁎/double vdata[]={15,100,90,60,40,15,10, 1};/⁎ Input data: VALUE ⁎/double wdata[]={ 2, 20,20,30,40,30,60,10};/⁎ Input data: WEIGHT ⁎/int datasize=8;
struct SolArray{
const char ⁎ind;double val;

Fair Isaac Corporation Conﬁdential and Proprietary Information/⁎ Array to receive solution values: ⁎//⁎index name ⁎//⁎solution value ⁎/112

C interface};
struct SolArray ⁎solution;
int solsize;
int main(){
XPRMmodel mod;int i,result;char data_name[40];char solution_name[40];char params[96];if(XPRMinit())
return 1;/⁎ File name of input data 'data' ⁎//⁎ File name of solution values ⁎//⁎ Parameter string for model execution ⁎//⁎ Initialize Mosel ⁎//⁎ Prepare file names for 'initializations' using the 'cb' driver ⁎/sprintf(data_name, "cb:%p", cbinit_from);
sprintf(solution_name, "cb:%p", cbinit_to);/⁎ Pass file names as execution param.s ⁎/sprintf(params, "DATAFILE='%s',SOLFILE='%s'", data_name, solution_name);if(XPRMexecmod(NULL, "burglar13.mos", params, &result, &mod))return 2;/⁎ Execute a model file ⁎/if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
return 3;/⁎ Test whether a solution is found ⁎//⁎ Display solution values obtained from the model ⁎/printf("Objective value: %g\n", XPRMgetobjval(mod));for(i=0;i<solsize;i++)printf(" take(%s): %g\n", solution[i].ind, solution[i].val);
XPRMresetmod(mod);return 0;}

The function for dynamic output retrieval employs the Mosel library functions that we have already seenin Section 13.3 for models after their termination. The prototype of the function cbinit_to needs tobe exactly as shown below.
int XPRM_RTC cbinit_to(XPRMcbinit cb, void ⁎info, const char ⁎label,
int type, XPRMalltypes ⁎ref){
XPRMarray solarr;
XPRMset sets[1];int indices[1];XPRMalltypes rvalue;
int ct;if(strcmp(label,"SOL")==0){
solarr=ref->array;solsize=XPRMgetarrsize(solarr);solution = (struct SolArray ⁎)malloc(solsize ⁎ sizeof(struct SolArray));
XPRMgetarrsets(solarr,sets);/⁎ Get the indexing sets
(we know array has 1 dimension) ⁎/ct=0;
XPRMgetfirstarrtruentry(solarr,indices); /⁎ Get the first true index tuple ⁎/do{solution[ct].ind=XPRMgetelsetval(sets[0],indices[0],&rvalue)->string;
XPRMgetarrval(solarr,indices,&rvalue);solution[ct].val=rvalue.real;

Fair Isaac Corporation Conﬁdential and Proprietary Information

113

C interface
ct++;} while(!XPRMgetnextarrtruentry(solarr,indices));}
else{
printf("Unknown output data item: %s %p\n", label, ref);}return 0;}

The dynamic data input to a Mosel model uses a new set of dedicated library functions.
The format used to represent data is the same as the default text format used by initializations
blocks. For example, the array deﬁnition
mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ]is represented by the following sequence of function calls:
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_OPENNDX, 0);
XPRMcb_sendstring(cb, "ind1", 0);
XPRMcb_sendint(cb, 3, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSENDX, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);
XPRMcb_sendint(cb, 5, 0);
XPRMcb_sendreal(cb, 1.2, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_OPENNDX, 0);
XPRMcb_sendstring(cb, "ind2", 0);
XPRMcb_sendint(cb, 7, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSENDX, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);
XPRMcb_sendint(cb, 4, 0);
XPRMcb_sendreal(cb, 6.5, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);

! [
!
(
!
"ind1"
!
3
!
)
!
[
!
5
!
1.2
!
]
!
(
!
"ind2"
!
7
!
)
!
[
!
4
!
6.5
!
]
! ]

The last argument ’0’ in these functions indicates that data is to be processed not immediately but only
once the queue of tokens is full.
For our example, we thus have the following function deﬁnition (again, the prototype of the callback
function must be deﬁned exactly to the form expected by Mosel):int XPRM_RTC cbinit_from(XPRMcbinit cb, void ⁎info, const char ⁎label,
int type, void ⁎ref){int i;
if(strcmp(label,"DATA")==0){
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);for(i=0;i<datasize;i++){
XPRMcb_sendctrl(cb, XPRM_CBC_OPENNDX, 0);
XPRMcb_sendstring(cb, ind[i], -1, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSENDX, 0);
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);
XPRMcb_sendreal(cb, vdata[i], 0);
XPRMcb_sendreal(cb, wdata[i], 0);
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);}
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);
return 0;}
else

Fair Isaac Corporation Conﬁdential and Proprietary Information

114

C interface{
fprintf(stderr,"Label `%s' not found.\n",label);
return 1;}}

The model ﬁle burglar13.mos receives through its run-time parameters the callback functions thatare to be used for data input/output in the initializations sections. The deﬁnition of themathematical model is the same as in the previous model version and left out in the listing below.
model Burglar13
uses "mmxprs"
parameters
DATAFILE = ''SOLFILE = ''WTMAX = 102
end-parameters! Location of input data
! Location for solution data output! Maximum weight allowed

declarations
ITEMS: set of string
VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of realsoltake: array(ITEMS) of real
end-declarations! Index set for items! Value of items! Weight of items! Solution values

initializations from DATAFILE[VALUE,WEIGHT] as "DATA"
end-initializations...
initializations to SOLFILE
soltake as "SOL"
end-initializations
end-model

13.4.4

Scalars
Besides arrays one might also wish to simply exchange scalars between the calling application and a
Mosel model. One way of passing the value of a scalar to a model is to deﬁne it as a model parameterand pass the new value as an execution parameter of the model (as shown in Section 13.2).
Alternatively, we might read or write scalar values in initializations blocks similarly to what wehave seen in the previous section for arrays.
Consider the following C program: there are three scalars, wmax, numitem, and objval. The value ofthe ﬁrst should be read in by the Mosel model and the last two receive solution values from theoptimization run in the model.
#include <stdio.h>#include "xprm_mc.h"
int wmax=100;
int numitem;
double objval;
int main(){
XPRMmodel mod;int result;char wmax_name[40];char num_name[40];char sol_name[40];char params[160];

Fair Isaac Corporation Conﬁdential and Proprietary Information/⁎ File name of input data 'wmax' ⁎//⁎ File name of output data 'num' ⁎//⁎ File name of solution value ⁎//⁎ Parameter string for model execution ⁎/115

C interface

if(XPRMinit()) return 1;/⁎ Initialize Mosel ⁎//⁎ Prepare file names for 'initializations' using the 'raw' driver ⁎/sprintf(wmax_name, "mem:%p/%d", &wmax, (int)sizeof(wmax));
sprintf(num_name, "mem:%p/%d", &numitem, (int)sizeof(numitem));sprintf(solution_name, "mem:%p/%d", &objval, (int)sizeof(objval));/⁎ Pass file names as execution param.s ⁎/sprintf(params, "WMAX='%s',NUM='%s',SOLVAL='%s'", wmax_name, num_name,sol_name);if(XPRMexecmod(NULL, "burglar12.mos", params, &result, &mod))return 2;/⁎ Execute a model file ⁎/if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
return 3;/⁎ Test whether a solution is found ⁎//⁎ Display solution values obtained from the model ⁎/printf("Objective value: %g\n", objval);printf("Total number of items: %d\n", numitem);
XPRMresetmod(mod);return 0;}

The Mosel model takes as execution parameters the ﬁlenames (location in memory) of the threescalars. The value WTMAX is initialized from the data in the application and the two other locations are
written to in the initializations to block at the end of the model.
model Burglar12
uses "mmxprs"
parameters
NUM = ''SOLVAL = ''WMAX = ''end-parameters! Location for no. of items output! Location for objective value output! Maximum weight allowed

declarations
WTMAX: integer! Maximum weight allowed
ITEMS = {"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}! Index set for items
VALUE: array(ITEMS) of real
! Value of items
WEIGHT: array(ITEMS) of real
! Weight of items
soltake: array(ITEMS) of real
! Solution values
end-declarations
VALUE :: (["camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"])[15,100,90,60,40,15,10,1]
WEIGHT:: (["camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"])[2,20,20,30,40,30,60,10]
initializations from 'raw:'
WTMAX as WMAX
end-initializations
declarations
take: array(ITEMS) of mpvar
end-declarations! 1 if we take item i; 0 otherwise! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX

Fair Isaac Corporation Conﬁdential and Proprietary Information

116

C interface! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
maximize(MaxVal)! Solve the MIP-problem! Print out the solution
writeln("Solution:")forall(i in ITEMS) writeln(" take(", i, "): ", getsol(take(i)))! Output solution to calling application
initializations to 'raw:'
evaluation of getobjval as SOLVAL
evaluation of round(sum(i in ITEMS) getsol(take(i))) as NUM
end-initializations
end-model

13.5

Redirecting the Mosel output
When integrating a Mosel model into an application it may be desirable to be able to redirect any output
produced by the model to the application. This can be done by the means of a callback function. Thisfunction takes a predeﬁned signature as shown in the following C program. If it is called from outsideof the execution of any Mosel model, its parameter model will be NULL. In our example the callback
function preﬁxes the printout of every line of Mosel output with Mosel:.
#include <stdio.h>#include "xprm_mc.h"/⁎⁎⁎⁎ Callback function to handle output ⁎⁎⁎⁎/
long XPRM_RTC cbmsg(XPRMmodel model, void ⁎info, char ⁎buf, unsigned long size){
printf("Mosel: %.⁎s", (int)size, buf);
return 0;}
int main(){
int result;char outfile_name[40];if(XPRMinit())
return 1;/⁎ File name of output stream ⁎//⁎ Initialize Mosel ⁎//⁎ Prepare file name for output stream ⁎//⁎ using 'cb' driver⁎/sprintf(outfile_name, "cb:%p", cbmsg);/⁎ Set default output stream to callback ⁎/XPRMsetdefstream(NULL, XPRM_F_WRITE, outfile_name);/⁎ Execute = compile/load/run a model ⁎/if(XPRMexecmod(NULL, "burglar2.mos", NULL, &result, NULL))
return 2;
return 0;}

The same procedure that has been presented here for redirecting the Mosel output can also be applied
to redirect any error messages produced by Mosel—the only required modiﬁcation consists in replacing
the constant XPRM_F_WRITE by XPRM_F_ERROR in the argument of function XPRMsetdefstream.

Fair Isaac Corporation Conﬁdential and Proprietary Information

117

C interface

13.6

Problem solving in C with Xpress Optimizer
In certain cases, for instance if the user wants to re-use parts of algorithms that he has written in C for
the Xpress Optimizer, it may be necessary to pass from a problem formulation with Mosel to solving
the problem in C by direct calls to the Optimizer. The following example shows how this may be done
for the Burglar problem. We use a slightly modiﬁed version of the original Mosel model:
model Burglar4
uses "mmxprs"
declarations
WTMAX=102! Maximum weight allowed
ITEMS={"camera", "necklace", "vase", "picture", "tv", "video","chest", "brick"}! Index set for items
VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of real

! Value of items! Weight of items

take: array(ITEMS) of mpvar
end-declarations! 1 if we take item i; 0 otherwise

initializations from 'burglar.dat'
VALUE WEIGHT
end-initializations! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
setparam("XPRS_LOADNAMES", true)loadprob(MaxVal)! Enable loading of object names! Load problem into the optimizer

end-model

The procedure maximize to solve the problem has been replaced by loadprob. This procedure loads
the problem into the optimizer without solving it. We also enable the loading of names from Mosel into
the optimizer so that we may obtain an easily readable output.
The following C program ugxprs1.c reads in the Mosel model and solves the problem by direct calls
to Xpress Optimizer. To be able to address the problem loaded into the optimizer, we need to retrieve
the optimizer problem pointer from Mosel. Since this information is a parameter (XPRS_PROBLEM) thatis provided by module mmxprs, we ﬁrst need to obtain the reference of this library (by using function
XPRMfinddso).
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "xprm_rt.h"#include "xprs.h"
int main(){
XPRMmodel mod;
XPRMdsolib dso;
XPRMalltypes rvalue;
XPRSprob prob;
int result, ncol, len, i;
double ⁎sol, val;char ⁎names, ⁎onecol;

Fair Isaac Corporation Conﬁdential and Proprietary Information

118

C interface

if(XPRMinit())
return 1;/⁎ Initialize Mosel ⁎/if((mod=XPRMloadmod("burglar4.bim", NULL))==NULL)
return 2;
if(XPRMrunmod(mod, &result, NULL))
return 3;/⁎ Load a BIM file ⁎//⁎ Run the model (no optimization) ⁎//⁎ Retrieve the pointer to the problem loaded in the Optimizer ⁎/if((dso=XPRMfinddso("mmxprs"))==NULL)
return 4;
if(XPRMgetdsoparam(mod, dso, "xprs_problem", &result, &rvalue))
return 5;
prob=(XPRSprob)strtoul(rvalue.ref,NULL,0);
XPRSchgobjsense(prob, XPRS_OBJ_MAXIMIZE);
if(XPRSmipoptimize(prob, ""))
return 6;/⁎ Set sense to maximization ⁎//⁎ Solve the problem ⁎/if(XPRSgetintattrib(prob, XPRS_MIPSTATUS, &result))
return 7;/⁎ Test whether a solution is found ⁎/if((result==XPRS_MIP_SOLUTION) || (result==XPRS_MIP_OPTIMAL)){
if(XPRSgetdblattrib(prob, XPRS_MIPOBJVAL, &val))
return 8;
printf("Objective value: %g\n", val); /⁎ Print the objective function value ⁎/if(XPRSgetintattrib(prob, XPRS_ORIGINALCOLS, &ncol))return 9;if((sol = (double ⁎)malloc(ncol ⁎ sizeof(double)))==NULL)
return 10;
if(XPRSgetmipsol(prob, sol, NULL))
return 11;/⁎ Get the primal solution values ⁎/if(XPRSgetnamelist(prob, 2, NULL, 0, &len, 0, ncol-1))
return 11;/⁎ Get the name array length ⁎/if((names = (char ⁎)malloc(len⁎sizeof(char)))==NULL)
return 12;
if(XPRSgetnamelist(prob, 2, names, len, NULL, 0, ncol-1))
return 13;/⁎ Get the variable names ⁎/onecol = names;for(i=0; i<ncol; i++) {/⁎ Print out the solution ⁎/printf("%s: %g\n", onecol, sol[i]);onecol = onecol+strlen(onecol)+1;}
free(names);
free(sol);}
return 0;}

Since the Mosel language provides ample programming facilities, in most applications there will be noneed to switch from the Mosel language to problem solving in C. If nevertheless this type of
implementation is chosen, it should be noted that it is not possible to get back to Mosel, once theXpress Optimizer has been called directly from C: the solution information and any possible changes
made to the problem directly in the optimizer are not communicated to Mosel.

Fair Isaac Corporation Conﬁdential and Proprietary Information

119

CHAPTER 14

Other programming language interfaces
In this chapter we show how the examples from Chapter 13 may be written with other programming
languages, namely Java, .NET and VBA.

14.1

Java
To use the Mosel Java classes the line import com.dashoptimization.⁎; must be added at the
beginning of the program.

14.1.1

Compiling and executing a model in Java
With Java Mosel is initialized by creating a new instance of class XPRM. To execute a Mosel model inJava we call the three Mosel functions performing the standard compile/load/run sequence as shownin the following example (ﬁle ugcomp.java).
import com.dashoptimization.⁎;public class ugcomp{
public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;mosel = new XPRM();// Initialize Mosel

System.out.println("Compiling `burglar2'");
mosel.compile("burglar2.mos");
System.out.println("Loading `burglar2'");
mod = mosel.loadModel("burglar2.bim");
System.out.println("Executing `burglar2'");
mod.run();System.out.println("`burglar2' returned: " + mod.getResult());}}

14.1.2

Termination
If the model execution is embedded in a larger appplication it may be useful to reset the model after its
execution to free some resources allocated to it:
mod.reset();Fair Isaac Corporation Conﬁdential and Proprietary Information// Reset the model120

Other programming language interfaces

This will release all intermediate objects created during the execution without deleting the model itself.
It is also possible to explicitly remove the temporary directory/ﬁles created by the execution of Mosel:
mosel.removeTmpDir();// Delete temporary files

Unloading models or Mosel from memory is ensured through standard ﬁnalization + garbage collection
functionalities of Java. The ﬁnalizers are public and may be called from the user’s Java program.
Finalization of Mosel only takes effect once all loaded models have been ﬁnalized. Finalizing Mosel
also removes the temporary directory/ﬁles created by the execution of Mosel.
mod.finalize();
mod = null;mosel.finalize();mosel = null;

14.1.3// Finalize a model// Finalize Mosel

Parameters
When executing a Mosel model in Java, it is possible to pass new values for its parameters into themodel. If, for instance, we want to run model ‘Prime’ from Section 8.3 to obtain all prime numbers up to500 (instead of the default value 100 set for the parameter LIMIT in the model), we may write thefollowing program:
import com.dashoptimization.⁎;public class ugparam{
public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;int LIM=500;
mosel = new XPRM();// Initialize Mosel

System.out.println("Compiling `prime'");mosel.compile("prime.mos");
System.out.println("Loading `prime'");mod = mosel.loadModel("prime.bim");
System.out.println("Executing `prime'");mod.execParams = "LIMIT=" + LIM;
mod.run();System.out.println("`prime' returned: " + mod.getResult());}}

Using the Mosel Java interface, it is not only possible to compile and run models, but also to accessinformation on the different modeling objects as is shown in the following sections.

14.1.4

Accessing sets
A complete version of a program (ﬁle ugparam.java) for running the model ‘Prime’ may look as
follows (we work with a model prime2 that corresponds to the one printed in Section 8.3 but with all
output printing removed because we are doing this in Java, and all entities accessed from Java are
explicitly declared as public):
import com.dashoptimization.⁎;public class ugparam

Fair Isaac Corporation Conﬁdential and Proprietary Information

121

Other programming language interfaces{
public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;
XPRMSet set;
int LIM=500, first, last;
mosel = new XPRM();// Initialize Mosel

System.out.println("Compiling `prime2'");
mosel.compile("prime2.mos");
System.out.println("Loading `prime2'");
mod = mosel.loadModel("prime2.bim");
System.out.println("Executing `prime2'");
mod.execParams = "LIMIT=" + LIM;
mod.run();System.out.println("`prime2' returned: " + mod.getResult());
set=(XPRMSet)mod.findIdentifier("SPrime");// Get the object 'SPrime'// it must be a set

if(!set.isEmpty()){
first = set.getFirstIndex();// Get the number of the first index
last = set.getLastIndex();// Get the number of the last index
System.out.println("Prime numbers from 2 to " + LIM);
for(int i=first;i<=last;i++)// Print all set elements
System.out.print(" " + set.getAsInteger(i) + ",");
System.out.println();}}}

To print the contents of set SPrime that contains the desired result (prime numbers between 2 and500), we retrieve the Mosel object of this name using method findIdentifier. If this set is not
empty, then we enumerate the elements of the set (using methods getFirstIndex and
getLastIndex to obtain the index range).

14.1.5Retrieving solution values
The following program ugsol.java executes the model ‘Burglar3’ (the same as model ‘Burglar2’ from
Chapter 2 but with all output printing removed and all model entities declared as public) and prints
out its solution.
import com.dashoptimization.⁎;public class ugsol{
public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;
XPRMArray varr, darr;
XPRMMPVar x;
XPRMSet set;
int[] indices;
double val;
mosel = new XPRM();// Initialize Mosel

mosel.compile("burglar3.mos");// Compile, load & run the model
mod = mosel.loadModel("burglar3.bim");mod.run();Fair Isaac Corporation Conﬁdential and Proprietary Information

122

Other programming language interfaces

if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
System.exit(1);// Stop if no solution found
System.out.println("Objective value: " + mod.getObjectiveValue());// Print the objective function value
varr=(XPRMArray)mod.findIdentifier("take");// Get model object 'take',// it must be an array
darr=(XPRMArray)mod.findIdentifier("VALUE"); // Get model object 'VALUE',// it must be an array
set=(XPRMSet)mod.findIdentifier("ITEMS");// Get model object 'ITEMS',// it must be a set
indices = varr.getFirstIndex();// Get the first entry of array varr// (we know that the array is dense)do{x = varr.get(indices).asMPVar();// Get a variable from varr
val = darr.getAsReal(indices);// Get the corresponding value
System.out.println("take(" + set.get(indices[0]) + "): " +x.getSolution() + "\t (item value: " + val + ")");// Print the solution value} while(varr.nextIndex(indices));// Get the next index
mod.reset();}}// Reset the model

The array of variables varr is enumerated using the array functions getFirstIndex and
nextIndex. These methods may be applied to arrays of any type and dimension. With these functions
we run systematically through all possible combinations of index tuples, hence the hint at dense arraysin the example. In the case of sparse arrays it is preferrable to use different enumeration functions that
only enumerate those entries that are deﬁned (see next section).

14.1.6Sparse arrays
We now again work with the problem ‘Transport’ that has been introduced in Chapter 3. The objectiveof this problem is to calculate the ﬂows ﬂowpr from a set of plants to a set of sales regions that satisfy
all demand and supply constraints and minimize the total cost. Not all plants may deliver goods to all
regions. The ﬂow variables ﬂowpr are therefore deﬁned as a sparse array. The following example
ugarray.java prints out all existing entries of the array of variables.
import com.dashoptimization.⁎;public class ugarray{public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;
XPRMArray varr;
XPRMSet[] sets;
int[] indices;int dim;mosel = new XPRM();// Initialize Mosel

mosel.compile("transport.mos");// Compile, load & run the model
mod = mosel.loadModel("transport.bim");mod.run();varr=(XPRMArray)mod.findIdentifier("flow"); // Get model object 'flow'// it must be an arraydim = varr.getDimension();// Get the number of dimensions// of the array

Fair Isaac Corporation Conﬁdential and Proprietary Information

123

Other programming language interfaces
sets = varr.getIndexSets();// Get the indexing sets

indices = varr.getFirstTEIndex();// Get the first true entry indexdo{System.out.print("flow(");
for(int i=0;i<dim-1;i++)System.out.print(sets[i].get(indices[i]) + ",");
System.out.print(sets[dim-1].get(indices[dim-1]) + "), ");} while(varr.nextTEIndex(indices)); // Get next true entry index tuple
System.out.println();mod.reset();}}// Reset the model

In this example, we ﬁrst get the number of indices (dimensions) of the array of variables varr (using
method getDimension). We use this information to enumerate the entries of every index tuple forgenerating a nicely formatted output. The array sets holds all the index sets of varr and the arrayindices corresponds to a single index tuple.
The enumeration starts with the ﬁrst deﬁned index tuple, obtained with method getFirstTEIndex,
and continues with a series of calls to nextTEIndex until all deﬁned entries have been enumerated.

14.1.7

Exchanging data between an application and a model
In the previous examples we have seen how to retrieve information about the model objects from a
Mosel model after its execution. In all cases the input data is deﬁned in the model itself or read in froman external (text) ﬁle. However, when embedding a model into an application frequently the input data
for the model will be stored (or generated by) the application itself. In such a case the user will certainly
wish a more immediate means of communication to the model than having to write the input data to an
external text ﬁle or database. In the following two subsections we therefore show how to pass data inmemory from an application to a Mosel model, and with the same mechanism (namely, using the jraw
I/O driver) from the model back to the calling application.
14.1.7.1

Dense arrays

As a ﬁrst example we shall look at the case of dense arrays holding the input and solution data. In theunderlying Mosel model this corresponds to arrays indexed by range sets that are known in the model
before the data are read in. In this example, we shall work with a version of the ‘Burglar model based on
the very ﬁrst version we have seen in Section 2.1 where all arrays are indexed by the range set ITEMS =1..8.
The following Java program ugiodense.java compiles, loads, and runs a Mosel model and thenprints out the solution values. The input data (arrays vdata and wdata) and the array solution thatis to receive the solution values are passed on to the model through model parameters.
Communication of the data between the application and the Mosel model is achieved through the jraw
I/O driver. File names for this driver have the form
jrawoption[,...],filename, where filename is an object reference. Since we are working with
dense, one-dimensional arrays we use the option noindex, indicating that only the data and not theindex tuples are to be exchanged.
import com.dashoptimization.⁎;public class ugiodense{// Input data
static final double[] vdata={15,100,90,60,40,15,10, 1};
static final double[] wdata={ 2, 20,20,30,40,30,60,10};// VALUE// WEIGHT// Array to receive solution values

Fair Isaac Corporation Conﬁdential and Proprietary Information

124

Other programming language interfaces
static double[] solution = new double[8];public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;mosel = new XPRM();// Initialize Mosel

mosel.compile("burglar8.mos");// Compile & load the model
mod = mosel.loadModel("burglar8.bim");// Associate the Java objects with names in Mosel
mosel.bind("vdat", vdata);
mosel.bind("wdat", wdata);
mosel.bind("sol", solution);// File names are passed through execution parameters
mod.execParams ="VDATA='noindex,vdat',WDATA='noindex,wdat',SOL='noindex,sol'";
mod.run();// Run the model

if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
System.exit(1);// Stop if no solution found// Display solution values obtained from the model
System.out.println("Objective value: " + mod.getObjectiveValue());
for(int i=0;i<8;i++)System.out.println(" take(" + (i+1) + "): " + solution[i]);mod.reset();}}// Reset the model

The model ﬁle burglar8.mos is the same as model burglar6.mos from Section 13.4.1 with the only
difference that the name of the I/O driver in the initializations blocks now is jraw instead ofraw, such as:
initializations from 'jraw:'
VALUE as VDATA WEIGHT as WDATA
end-initializations

14.1.7.2

Sparse arrays

Let us now study the probably more frequent case of data stored in sparse format. In the Mosel model(burglar9.mos) we use a set of strings instead of a simple range set to index the various arrays andin the Java program (ugiosparse.java) we need to deﬁne slightly more complicated structures tohold the indices and the data entries. To save us writing out the indices twice, we have grouped the two
input data arrays into a single class. When passing the data arrays to the Mosel model we now do not
use any option, meaning that data is transferred in sparse format. Instead, we now need to indicate
which ﬁelds of the Java objects are to be selected (in brackets after the object reference).
import com.dashoptimization.⁎;public class ugiosparse{// Class to store initial values for array 'data'
public static class MyData{
public String ind;// index namepublic double val,wght;// value and weight data entries
MyData(String i, double v, double w){ ind=i; val=v; wght=w; }}Fair Isaac Corporation Conﬁdential and Proprietary Information

125

Other programming language interfaces// Class to receive solution values
public static class MySol{
public String ind;// index namepublic double val;// solution value}
public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;MyData data[]={new MyData("camera",15,2), new MyData("necklace",100,20),
new MyData("vase",90,20), new MyData("picture",60,30),
new MyData("tv",40,40), new MyData("video",15,30),
new MyData("chest",10,60), new MyData("brick",1,10)};MySol[] solution=new MySol[8];for(int i=0;i<8;i++) solution[i] = new MySol();
mosel = new XPRM();// Initialize Mosel

mosel.compile("burglar9.mos");// Compile & load the model
mod = mosel.loadModel("burglar9.bim");// Associate the Java objects with names in Mosel
mosel.bind("dt", data);
mosel.bind("sol", solution);// File names are passed through execution parameters
mod.execParams = "DATA='dt(ind,val,wght)',SOL='sol(ind,val)'";mod.run();// Run the model

if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
System.exit(1);// Stop if no solution found// Display solution values obtained from the model
System.out.println("Objective value: " + mod.getObjectiveValue());
for(int i=0;i<8;i++)System.out.println(" take(" + solution[i].ind + "): " + solution[i].val);
mod.reset();}}// Reset the model

The model burglar9.mos run by this program is the same as the model burglar7.mos displayed in
Section 13.4.2, but using the I/O driver jraw instead of raw.
14.1.7.3

Dynamic data

The two examples of in-memory communication of dense and sparse data in the preceding sections
have in commun that all data structures in the application, and in particular the structures to receive
output data, are of ﬁxed size. We therefore now introduce an alternative communication mechanism
working with streams, that enables dynamic sizing of data structures on the application level, a featurethat is particularly useful for solution output where effective data sizes are not known a priori. This
communication mechanism employs the I/O driver java (see also Section 14.1.8). The main part of ourJava program (ﬁle ugiocb.java) now looks as follows.
public static modelInit cbinit=new modelInit();public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;mosel = new XPRM();

Fair Isaac Corporation Conﬁdential and Proprietary Information// Initialize Mosel

126

Other programming language interfaces

mosel.compile("burglar13.mos");// Compile & load the model
mod = mosel.loadModel("burglar13.bim");// File names are passed through execution parameters
mod.execParams = "DATAFILE='java:ugiocb.cbinit'," +"SOLFILE='java:ugiocb.cbinit'";mod.run();// Run the model

if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
System.exit(1);// Stop if no solution found// Display solution values obtained from the model
System.out.println("Objective value: " + mod.getObjectiveValue());
for(int i=0;i<solsize;i++)System.out.println(" take(" + solution[i].ind + "): " + solution[i].val);
mod.reset();}// Reset the model

The information passed to the model in the runtime parameters now is an instance of a class that
implements interfaces for initialization from and to streams as shown below. The functionality for
dynamic output retrieval employs the Mosel library functions that we have already seen in Sections
14.1.4 and 14.1.5 for accessing models after their termination. The dynamic data input to a Mosel model
uses a new set of dedicated functions that are explained with some more detail after the program
extract.
static final double[] vdata={15,100,90,60,40,15,10, 1};// VALUE
static final double[] wdata={ 2, 20,20,30,40,30,60,10};// WEIGHT
static final String[] ind={"camera", "necklace", "vase", "picture","tv", "video", "chest", "brick"};// Index names
static final int datasize=8;
public static class MySol {public String ind;public double val;}
static MySol[] solution;
static int solsize;// index name// solution value

public static class modelInit implements XPRMInitializationFrom, XPRMInitializationTo{
public boolean initializeTo(String label, XPRMValue value){
XPRMArray solarr;
XPRMSet[] sets;
int[] indices;int ct;if(label.equals("SOL")){solarr=(XPRMArray)value;solsize=solarr.getSize();solution = new MySol[solsize];for(int i=0;i<solsize;i++) solution[i] = new MySol();
sets = solarr.getIndexSets();// Get the indexing sets
ct=0;
indices = solarr.getFirstTEIndex();// Get the first entry of the arraydo{solution[ct].ind=sets[0].getAsString(indices[0]);solution[ct].val=solarr.getAsReal(indices);ct++;} while(solarr.nextTEIndex(indices)); // Get the next index}

Fair Isaac Corporation Conﬁdential and Proprietary Information

127

Other programming language interfaces
else System.out.println("Unknown output data item: " + label + "=" + value);
return true;}
public boolean initializeFrom(XPRMInitializeContext ictx, String label, XPRMTyped type){try{
if(label.equals("DATA")){
ictx.sendControl(ictx.CONTROL_OPENLST);
for(int i=0;i<datasize;i++){
ictx.sendControl(ictx.CONTROL_OPENNDX);
ictx.send(ind[i]);ictx.sendControl(ictx.CONTROL_CLOSENDX);
ictx.sendControl(ictx.CONTROL_OPENLST);
ictx.send(vdata[i]);ictx.send(wdata[i]);ictx.sendControl(ictx.CONTROL_CLOSELST);}
ictx.sendControl(ictx.CONTROL_CLOSELST);
return true;}
else{
System.err.println("Label `"+label+"' not found.");return false;}}
catch(java.io.IOException e){
System.err.println("`"+label+"' could not be initialized - "+e);return false;}}}

The format used to represent data for dynamic data input is the same as the default text format used byinitializations blocks. For example, the array deﬁnition
mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ]is represented by the following sequence of function calls:
ictx.sendControl(ictx.CONTROL_OPENLST);
ictx.sendControl(ictx.CONTROL_OPENNDX);
ictx.send("ind1");
ictx.send(3);ictx.sendControl(ictx.CONTROL_CLOSENDX);
ictx.sendControl(ictx.CONTROL_OPENLST);
ictx.send(5);ictx.send(1.2);
ictx.sendControl(ictx.CONTROL_CLOSELST);
ictx.sendControl(ictx.CONTROL_OPENNDX);
ictx.send("ind2");
ictx.send(7);ictx.sendControl(ictx.CONTROL_CLOSENDX);
ictx.sendControl(ictx.CONTROL_OPENLST);
ictx.send(4);ictx.send(6.5);
ictx.sendControl(ictx.CONTROL_CLOSELST);
ictx.sendControl(ictx.CONTROL_CLOSELST);

! [
!
(
!
"ind1"
!
3
!
)
!
[
!
5
!
1.2
!
]
!
(
!
"ind2"
!
7
!
)
!
[
!
4
!
6.5
!
]
! ]

The send and sendControl methods may take an additional last argument indicating whether data is
to be processed immediately or only once the queue of tokens is full (default).

Fair Isaac Corporation Conﬁdential and Proprietary Information

128

Other programming language interfaces

With Java, we use exactly the same model ﬁle burglar13.mos as with C (see Section 13.4.3 for thelisting).
14.1.7.4

Scalars

Besides arrays one might also wish to simply exchange scalars between the calling application and a
Mosel model. One way of passing the value of a scalar to a model is to deﬁne it as a model parameterand pass the new value as an execution parameter to the model (as shown in Section 14.1.3).
Alternatively, we might read or write scalar values in initializations blocks similarly to what wehave seen in the previous section for arrays.
Consider the following Java program: we wish to exchange the values of the three scalars, wmax,
numitem, and objval with the Mosel model run by this program. The value of the ﬁrst scalar should
be read in by the Mosel model and the last two receive solution values from the optimization run in themodel. Since it is not possible to address scalars directly from the model we have collected them into aclass MyData the ﬁelds of which are then speciﬁed in the execution parameters as the locations of thedata.
import com.dashoptimization.⁎;public class ugioscalar{public static class MyData{
public int wmax;
public int numitem;
public double objval;}// Scalars for data in/output

public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;MyData data=new MyData();data.wmax=100;
mosel = new XPRM();// Initialize Mosel

mosel.compile("burglar11.mos");// Compile & load the model
mod = mosel.loadModel("burglar11.bim");// Associate the Java object with a name in Mosel
mosel.bind("data", data);// File names are passed through execution parameters
mod.execParams ="WMAX='data(wmax)',NUM='data(numitem)',SOLVAL='data(objval)'";mod.run();// Run the model

if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
System.exit(1);// Stop if no solution found// Display solution values obtained from the model
System.out.println("Objective value: " + data.objval);
System.out.println("Total number of items: " + data.numitem);mod.reset();}}// Reset the model

The Mosel model burglar11.mos run by this program is the same as the model burglar12.mos
displayed in Section 13.4.4, but using the I/O driver jraw instead of raw. This model takes as execution

Fair Isaac Corporation Conﬁdential and Proprietary Information

129

Other programming language interfaces

parameters the ﬁlenames (location in memory) of the three scalars. The integer WTMAX is initialized
from the value in the Java application and the two other locations are written to in theinitializations to block at the end of the model.

14.1.8Redirecting the Mosel output
When executing a Mosel model from a Java application it may be desirable to be able to process theoutput produced by Mosel directly in the application. The following Java program ugcb.java shows acallback-style functionality that redirects the Mosel standard output to an OutputStream object which
is used to preﬁx every line of Mosel output with the string Mosel: before printing it.
To redirect Mosel streams to a Java object (Java streams or ByteBuffer) we need to use the java
I/O driver. The same mechanism that is used here for redirecting the output stream of Mosel (indicatedby XPRM.F_OUTPUT, with the additional option XPRM.F_LINBUF to enable line buffering) can equally
be used to redirect, for instance, the error stream (denoted by the constant XPRM.F_ERROR).
import java.io.⁎;import com.dashoptimization.⁎;public class ugcb{// OutputStream class to handle default output
public static class MyOut extends OutputStream{
public void flush(){ System.out.flush(); }public void write(byte[] b){
System.out.print("Mosel: ");System.out.write(b,0,b.length);}// These methods are not used by Mosel:
public void write(byte[] b,int off,int len) {}public void write(int b) {}public void close() {}}public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;
MyOut cbmsg = new MyOut();// Define output stream as "MyOut"
mosel = new XPRM();// Initialize Mosel

mosel.bind("mycb", cbmsg);// Associate Java object with a name in Mosel// Set default output stream to cbmsg
mosel.setDefaultStream(XPRM.F_OUTPUT|XPRM.F_LINBUF, "java:mycb");mosel.compile("burglar2.mos");// Compile, load & run the model
mod = mosel.loadModel("burglar2.bim");mod.run();
}
}

14.2

.NET
Example code in this guide will be in C#, however one can access the Mosel .NET interface via otherlanguages that target the .NET Framework, such as VB.NET.
To use the Mosel .NET classes the line using Mosel; must be added at the beginning of the
program, and your project should have a dependency on the Xpress ﬁle xprmdn.dll.

Fair Isaac Corporation Conﬁdential and Proprietary Information

130

Other programming language interfaces

14.2.1

Compiling and executing a model in C#
With C# Mosel is initialized by obtaining a new instance of class XPRM via the static method
XPRM.Init(). To execute a Mosel model in C# we call the three Mosel functions performing thestandard compile/load/run sequence as shown in the following example (ﬁle ugcomptmp.cs).
using System;
using System.IO;
using Mosel;
namespace ugcomptmp.cs {public class ugcomptmp {/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile the Mosel model, save the BIM file in Mosel's temp. dir.
mosel.Compile("", "burglar2.mos", "tmp:burglar2.bim");// Load the BIM file
XPRMModel model = mosel.LoadModel("tmp:burglar2.bim");}}// Run the model
model.Run();}14.2.2

Termination
If the model execution is embedded in a larger appplication it may be useful to reset the model after its
execution to free some resources allocated to it:
mod.Reset();// Reset the modelThis will release all intermediate objects created during the execution without deleting the model itself.
It is also possible to explicitly remove the temporary directory/ﬁles created by the execution of Mosel:
mosel.RemoveTmpDir();// Delete temporary files

Unloading models or Mosel from memory is ensured through standard disposal, ﬁnalization + garbage
collection functionalities of the .NET runtime. The disposal methods are public and may be called fromthe user’s C# program. Finalization of Mosel only takes effect once all loaded models have beenﬁnalized or disposed. Finalizing or diposing Mosel also removes the temporary directory/ﬁles createdby the execution of Mosel.
mod.Dispose();mod = null;mosel.Dispose();mosel = null;

14.2.3// Dispose a model// Dispose Mosel

Parameters
When executing a Mosel model in C#, it is possible to pass new values for its parameters into the

Fair Isaac Corporation Conﬁdential and Proprietary Information

131

Other programming language interfaces

model. If, for instance, we want to run model ‘Prime’ from Section 8.3 to obtain all prime numbers up to500 (instead of the default value 100 set for the parameter LIMIT in the model), we may write thefollowing program:using System;
using System.IO;
using Mosel;
namespace ugparam.cs {public class ugparam {/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {XPRMSet set;
int LIM=500, first, last;// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load a model
XPRMModel model = mosel.CompileAndLoad("prime.mos");

}

}

}

// Run the model
model.ExecParams = "LIMIT=" + LIM;
model.Run();
Console.WriteLine("`prime' returned: " + model.Result);

Using the Mosel .NET interface, it is not only possible to compile and run models, but also to accessinformation on the different modeling objects as is shown in the following sections.

14.2.4

Accessing sets
A complete version of a program (ﬁle ugparam.cs) for running the model ‘Prime’ may look as follows(we work with a model prime2 that corresponds to the one printed in Section 8.3 but with all output
printing removed because we are doing this in C#, and all entities accessed from C# are declared as
public):using System;
using System.IO;
using Mosel;
namespace ugparam.cs {public class ugparam {/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {XPRMSet set;
int LIM=500, first, last;// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load a model
XPRMModel model = mosel.CompileAndLoad("prime.mos");

Fair Isaac Corporation Conﬁdential and Proprietary Information

132

Other programming language interfaces// Run the model
model.ExecParams = "LIMIT=" + LIM;
model.Run();
Console.WriteLine("`prime' returned: " + model.Result);// Get model object 'SPrime', it must be a set
set=(XPRMSet)model.FindIdentifier("SPrime");

}

}

}

// Enumerate the set elementsif (!set.IsEmpty){
first = set.FirstIndex;// Get the number of the first index
last = set.LastIndex;// Get the number of the last index
Console.WriteLine("Prime numbers from 2 to " + LIM);
for (int i=first;i<=last;i++)// Print all set elements
Console.Write(" {0},", set.GetAsInteger(i));
Console.WriteLine();}To print the contents of set SPrime that contains the desired result (prime numbers between 2 and500), we retrieve the Mosel object of this name using method FindIdentifier. If this set is not
empty, then we enumerate the elements of the set (using properties FirstIndex and LastIndex toobtain the index range).

14.2.5Retrieving solution values
The following program ugsol.cs executes the model ‘Burglar3’ (the same as model ‘Burglar2’ from
Chapter 2 but with all output printing removed and all model entities declared as public) and prints
out its solution.
using System;
using System.IO;
using Mosel;
namespace ugsol.cs {public class ugsol {/// <summary>/// Main entry point for the application/// </summary>[STAThread]static int Main(string[] args) {XPRMArray varr, darr;
XPRMSet set;
XPRMMPVar x;
double val;// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load a model
XPRMModel model = mosel.CompileAndLoad("burglar3.mos");// Run the model
model.Run();if(model.ProblemStatus!=XPRMProblemStatus.PB_OPTIMAL)
return 1;// Stop if no solution found
Console.WriteLine("Objective value: " + model.ObjectiveValue);// Print the objective function value

Fair Isaac Corporation Conﬁdential and Proprietary Information

133

Other programming language interfaces// Get model object 'take', it must be an array
varr=(XPRMArray)model.FindIdentifier("take");// Get model object 'VALUE', it must be an array
darr=(XPRMArray)model.FindIdentifier("VALUE");// Get model object 'ITEMS', it must be a set
set=(XPRMSet)model.FindIdentifier("ITEMS");// Enumerate all entries of 'varr' (dense array)foreach(int[] indices in varr.Indices){x = varr.Get(indices).AsMPVar();// Get a variable from varr
val = darr.GetAsReal(indices);// Get the corresponding value
Console.WriteLine("take(" + set.GetAsString(indices[0]) + "): " +Console.WriteLine("take" + varr.IndexToString(indices) + ": " +x.Solution + "\t (item value: " + val + ")");
}

//

}

}

}

model.Reset();return 0;// Reset the model

The array of variables varr is enumerated using via the array Indices property. This may be applied
to arrays of any type and dimension. With these functions we run systematically through all possiblecombinations of index tuples, hence the hint at dense arrays in the example. In the case of sparsearrays it is preferrable to use a different enumeration property that only enumerate those entries thatare deﬁned (see next section).

14.2.6Sparse arrays
We now again work with the problem ‘Transport’ that has been introduced in Chapter 3. The objectiveof this problem is to calculate the ﬂows ﬂowpr from a set of plants to a set of sales regions that satisfy
all demand and supply constraints and minimize the total cost. Not all plants may deliver goods to all
regions. The ﬂow variables ﬂowpr are therefore deﬁned as a sparse array. The following example
ugarray.cs prints out all existing entries of the array of variables.
using System;
using System.IO;
using Mosel;
namespace ugarray.cs {public class ugarray {/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {XPRMArray varr;
XPRMSet[] sets;
XPRMValue[] vindex;
int dim;// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load a model
XPRMModel model = mosel.CompileAndLoad("transport.mos");// Run the model
model.Run();// Get model object 'flow', it must be an array

Fair Isaac Corporation Conﬁdential and Proprietary Information

134

Other programming language interfaces
varr=(XPRMArray)model.FindIdentifier("flow");
dim = varr.Dim;// Get the number of dimensions of the arraysets = varr.IndexSets;// Get the indexing sets// Enumerate over the true entries
foreach(int[] indices in varr.TEIndices){// Get the values for this index
vindex = varr.DereferenceIndex(indices);
Console.Write("flow(");
for(int i=0;i<dim-1;i++)Console.Write(vindex[i] + ",");
Console.Write(vindex[dim-1] + "), ");// Alternative printing method:// Console.Write("flow" + varr.IndexToString(indices) + ", ");}Console.WriteLine();

}

}

}

model.Reset();// Reset the model

In this example, we ﬁrst get the number of indices (dimensions) of the array of variables varr (using
property Dim). We use this information to enumerate the entries of every index tuple for generating anicely formatted output. The array sets holds all the index sets of varr and the array indicescorresponds to a single index tuple.
The enumeration runs over all the deﬁned index tuples, obtained with property TEIndices.

14.2.7

Exchanging data between an application and a model
In the previous examples we have seen how to retrieve information about the model objects from a
Mosel model after its execution. In all cases the input data is deﬁned in the model itself or read in froman external (text) ﬁle. However, when embedding a model into an application frequently the input data
for the model will be stored (or generated by) the application itself. In such a case the user will certainly
wish a more immediate means of communication to the model than having to write the input data to an
external text ﬁle or database. In the following two subsections we therefore show how to pass data inmemory from an application to a Mosel model, and with the same mechanism (namely, using thedotnetraw I/O driver) from the model back to the calling application.
14.2.7.1

Dense arrays

As a ﬁrst example we shall look at the case of dense arrays holding the input and solution data. In theunderlying Mosel model this corresponds to arrays indexed by range sets that are known in the model
before the data are read in. In this example, we shall work with a version of the Burglar model based on
the very ﬁrst version we have seen in Section 2.1 where all arrays are indexed by the range set ITEMS =1..8.
The following C# program ugiodense.cs compiles, loads, and runs a Mosel model and then prints
out the solution values. The input data (arrays vdata and wdata) and the array solution that is toreceive the solution values are passed on to the model through model parameters. Communication of
the data between the application and the Mosel model is achieved through the dotnetraw I/O driver.
File names for this driver have the form
dotnetrawoption[,...],filename, where filename is an object reference. Since we areworking with dense, one-dimensional arrays we use the option noindex, indicating that only the dataand not the index tuples are to be exchanged.
using System;

Fair Isaac Corporation Conﬁdential and Proprietary Information

135

Other programming language interfaces
using System.IO;
using Mosel;
namespace ugiodense.cs {public class ugiodense {/// <summary>/// Arrays containing initialization data for the model/// </summary>static double[] vdata = new double[] {15,100,90,60,40,15,10, 1};
static double[] wdata = new double[] { 2, 20,20,30,40,30,60,10};// VALUE// WEIGHT/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load the Mosel model
XPRMModel model = mosel.CompileAndLoad("burglar8d.mos");// Associate the .NET objects with names in Mosel
model.Bind("vdat", vdata);
model.Bind("wdat", wdata);// Create a new array for solution data and bind that to the name 'SOL'
double[] solution = new double[8];mosel.Bind("sol", solution);// Pass data location as a parameter to the model
model.ExecParams = "VDATA='noindex,vdat',WDATA='noindex,wdat',SOL='noindex,sol'";// Run the model
model.Run();}}// Print the solution
Console.WriteLine("Objective value: {0}", model.ObjectiveValue);for (int i=0;i<8;i++)Console.Write(" take({0}): {1}", (i+1), solution[i]);Console.WriteLine();}

The model ﬁle burglar8d.mos is the same as model burglar6.mos from Section 13.4.1 with the
only difference that the name of the I/O driver in the initializations blocks now is dotnetraw
instead of raw, such as:
initializations from 'dotnetraw:'
VALUE as VDATA WEIGHT as WDATA
end-initializations

14.2.7.2

Sparse arrays

Let us now study the probably more frequent case of data stored in sparse format. In the Mosel model(burglar9d.mos) we use a set of strings instead of a simple range set to index the various arrays andin the C# program (ugiosparse.cs) we need to deﬁne slightly more complicated structures to holdthe indices and the data entries. To save us writing out the indices twice, we have grouped the two
input data arrays into a single class. When passing the data arrays to the Mosel model we now do not
use any option, meaning that data is transferred in sparse format. Instead, we now need to indicate

Fair Isaac Corporation Conﬁdential and Proprietary Information

136

Other programming language interfaces

which ﬁelds of the C# objects are to be selected (in brackets after the object reference).
using System;
using System.IO;
using Mosel;
namespace ugiosparse.cs {public class ugiosparse {/// <summary>/// Arrays containing initialization data for the model/// </summary>static double[] vdata = new double[] {15,100,90,60,40,15,10, 1};
static double[] wdata = new double[] { 2, 20,20,30,40,30,60,10};// VALUE// WEIGHT/// <summary>/// Structure to store initial values for the array 'data'/// </summary>class MyData {public string ind;public double val;public double wght;}

public MyData(string i, double v, double w) {this.ind = i;this.val = v;
this.wght = w;}/// <summary>/// Structure to receive solution values/// </summary>class MySol {public string ind;public double val;}/// <summary>/// The initial values for the array 'data'/// </summary>private static MyData[] data = new MyData[] {new MyData("camera",15,2), new MyData("necklace",100,20),
new MyData("vase",90,20), new MyData("picture",60,30),
new MyData("tv",40,40), new MyData("video",15,30),
new MyData("chest",10,60), new MyData("brick",1,10) };/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load the Mosel model
XPRMModel model = mosel.CompileAndLoad("burglar9d.mos");// Associate the .NET object with a name in Mosel
model.Bind("dt", data);// Create a new array for solution data and bind that to the name 'SOL'
MySol[] solution=new MySol[8];for(int i=0;i<8;i++) solution[i] = new MySol();
mosel.Bind("sol", solution);

Fair Isaac Corporation Conﬁdential and Proprietary Information

137

Other programming language interfaces// Pass data location as a parameter to the model
model.ExecParams = "DATA='dt(ind,val,wght)',SOL='sol(ind,val)'";// Run the model
model.Run();}// Print the solution
Console.WriteLine("Objective value: {0}", model.ObjectiveValue);for (int i=0;i<8;i++)Console.Write(" take({0}): {1}", solution[i].ind, solution[i].val);
Console.WriteLine();}}

The model burglar9d.mos run by this program is the same as the model burglar7.mos displayedin Section 13.4.2, but using the I/O driver dotnetraw instead of raw.
14.2.7.3

Dynamic data

The two examples of in-memory communication of dense and sparse data in the preceding sections
have in commun that all data structures in the application, and in particular the structures to receive
output data, are of ﬁxed size. We therefore now introduce an alternative communication mechanism
working with streams, that enables dynamic sizing of data structures on the application level, a featurethat is particularly useful for solution output where effective data sizes are not known a priori. This
communication mechanism employs the I/O driver dotnet (see also Section 14.2.8). The main part of
our C# program (ﬁle ugiocb.cs) now looks as follows.
[STAThread]static int Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load the Mosel model
XPRMModel model = mosel.CompileAndLoad("burglar13.mos");// Set the execution parameters and bind the variables
model.SetExecParam("DATAFILE","dotnet:cbinitfrom");model.SetExecParam("SOLFILE","dotnet:cbinitto");model.Bind("cbinitfrom", new XPRMInitializationFrom(initializeFrom));model.Bind("cbinitto", new XPRMInitializationTo(initializeTo));// Run the model
model.Run();if(model.ProblemStatus!=XPRMProblemStatus.PB_OPTIMAL)
return 1;// Stop if no solution found// Display solution values obtained from the model
Console.WriteLine("Objective value: {0}", model.ObjectiveValue);for(int i=0;i<solsize;i++)Console.WriteLine(" take({0}): {1}", solution[i].ind, solution[i].val);}model.Reset();return 0;// Reset the model

The information passed to the model in the runtime parameters are now instances of delegates for
initialization from and to streams as shown below. The functionality for dynamic output retrieval
employs the Mosel library functions that we have already seen in Sections 14.2.4 and 14.2.5 foraccessing models after their termination. The dynamic data input to a Mosel model uses a new set of
dedicated functions that are explained with some more detail after the program extract.

Fair Isaac Corporation Conﬁdential and Proprietary Information

138

Other programming language interfaces
static double[] vdata=new double[] {15,100,90,60,40,15,10, 1}; // VALUE
static double[] wdata=new double[] { 2, 20,20,30,40,30,60,10}; // WEIGHT
static string[] ind=new string[] {"camera", "necklace", "vase", "picture","tv", "video", "chest", "brick"};// Index names
static int datasize=8;/// <summary>/// Structure to receive solution values/// </summary>class MySol {public string ind;// index namepublic double val;// solution value}
static MySol[] solution;
static int solsize;/// <summary>/// A function to initialize the Mosel data-structures via callback/// </summary>public static bool initializeFrom(XPRMInitializeContext ictx,string label,XPRMTyped type){
try {
switch (label) {case "DATA":
ictx.Send(XPRMInitializeControl.OpenList);for (int i=0;i<datasize;i++) {ictx.Send(XPRMInitializeControl.OpenIndices);
ictx.Send(ind[i]);ictx.Send(XPRMInitializeControl.CloseIndices);
ictx.Send(XPRMInitializeControl.OpenList);
ictx.Send(vdata[i]);ictx.Send(wdata[i]);ictx.Send(XPRMInitializeControl.CloseList);}
ictx.Send(XPRMInitializeControl.CloseList);
return true;default:
Console.WriteLine("Label '{0}' not found", label);
return false;}} catch (Exception e) {Console.WriteLine("Label '{0}' could not be initialized - {1}", label, e.Message);
return false;}}///// <summary>/// A method to retrieve data from Mosel/// </summary>public static bool initializeTo(string label,XPRMValue val) {Console.WriteLine(".NET: {0} = {1}", label, val);
XPRMArray solarr;
XPRMValue[] vindex;
switch (label) {case "SOL":
solarr=(XPRMArray)val;
solsize=solarr.Size;
solution = new MySol[solsize];for(int i=0;i<solsize;i++) solution[i] = new MySol();
int ct=0;// Enumerate solarr as sparse arrayforeach(int [] indices in solarr.TEIndices) {vindex = solarr.DereferenceIndex(indices);
solution[ct].ind = vindex[0].AsString();
solution[ct].val = solarr.GetAsReal(indices);

Fair Isaac Corporation Conﬁdential and Proprietary Information

139

Other programming language interfaces

}

}

ct++;
}
return true;default:
Console.WriteLine("Unknown output data item: '{0}'={1} not found", label, val);
return false;

The format used to represent data for dynamic data input is the same as the default text format used byinitializations blocks. For example, the array deﬁnition
mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ]is represented by the following sequence of function calls:
ictx.Send(XPRMInitializeControl.OpenList);
ictx.Send(XPRMInitializeControl.OpenIndices);
ictx.Send("ind1");
ictx.Send(3);ictx.Send(XPRMInitializeControl.CloseIndices);
ictx.Send(XPRMInitializeControl.OpenList);
ictx.Send(5);ictx.Send(1.2);
ictx.Send(XPRMInitializeControl.CloseList);
ictx.Send(XPRMInitializeControl.OpenIndices);
ictx.Send("ind2");
ictx.Send(7);ictx.Send(XPRMInitializeControl.CloseIndices);
ictx.Send(XPRMInitializeControl.OpenList);
ictx.Send(4);ictx.Send(6.5);
ictx.Send(XPRMInitializeControl.CloseList);
ictx.Send(XPRMInitializeControl.CloseList);

! [
!
(
!
"ind1"
!
3
!
)
!
[
!
5
!
1.2
!
]
!
(
!
"ind2"
!
7
!
)
!
[
!
4
!
6.5
!
]
! ]

The send and sendControl methods may take an additional last argument indicating whether data is
to be processed immediately or only once the queue of tokens is full (default).
With C#, we use exactly the same model ﬁle burglar13.mos as with C (see Section 13.4.3 for thelisting).
14.2.7.4

Scalars

Besides arrays one might also wish to simply exchange scalars between the calling application and a
Mosel model. One way of passing the value of a scalar to a model is to deﬁne it as a model parameterand pass the new value as an execution parameter to the model (as shown in Section 14.2.3).
Alternatively, we might read or write scalar values in initializations blocks similarly to what wehave seen in the previous section for arrays.
Consider the following C# program: we wish to exchange the values of the three scalars, wmax,
numitem, and objval with the Mosel model run by this program. The value of the ﬁrst scalar should
be read in by the Mosel model and the last two receive solution values from the optimization run in themodel. Since it is not possible to address scalars directly from the model we have collected them into aclass MyData the ﬁelds of which are then speciﬁed in the execution parameters as the locations of thedata.
using System;
using System.IO;
using Mosel;

Fair Isaac Corporation Conﬁdential and Proprietary Information

140

Other programming language interfaces
namespace ugioscalar.cs {public class ugioscalar {/// <summary>/// Structure to receive solution values/// </summary>class MyData {public int wmax;
public int numitem;
public double objval;}/// <summary>/// Main entry point for the application/// </summary>[STAThread]static int Main(string[] args) {MyData data=new MyData();data.wmax=100;// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load a model
XPRMModel model = mosel.CompileAndLoad("burglar11.mos");// Associate the .NET object with a name in Mosel
model.Bind("data", data);// Run the model, passing data location as parametersmodel.ExecParams ="WMAX='data(wmax)',NUM='data(numitem)',SOLVAL='data(objval)'," +"IODRV='dotnetraw:'";model.Run();if(model.ProblemStatus!=XPRMProblemStatus.PB_OPTIMAL)
return 1;// Stop if no solution found// Display solution values obtained from the model
Console.WriteLine("Objective value: " + data.objval);
Console.WriteLine("Total number of items: " + data.numitem);

}

}

}

model.Reset();return 0;// Reset the model

The Mosel model burglar11.mos run by this program is the same as the model burglar12.mos
displayed in Section 13.4.4, but using the I/O driver dotnetraw instead of raw (which we set throughthe IODRV parameter). This model takes as execution parameters the ﬁlenames (location in memory)of the three scalars. The integer WTMAX is initialized from the value in the .NET application and the twoother locations are written to in the initializations to block at the end of the model.

14.2.8Redirecting the Mosel output
When executing a Mosel model from a .NET application it may be desirable to be able to process theoutput produced by Mosel directly in the application. The following C# program ugcb.cs shows acallback-style functionality that redirects the Mosel standard output to a TextWriter object which isused to preﬁx every line of Mosel output with the string Mosel: before printing it.
To redirect Mosel streams to a .NET object (.NET Stream (including MemoryStream for in-memory
buffers), TextReader, or TextWriter) we need to use the dotnet I/O driver. The same mechanismthat is used here for redirecting the output stream of Mosel (indicated by
XPRMStreamType.F_OUTPUT_LINEBUF which also enables line buffering) can equally be used toFair Isaac Corporation Conﬁdential and Proprietary Information

141

Other programming language interfaces

redirect, for instance, the error stream (denoted by the constant XPRMStreamType.F_ERROR).
Note that text read from a TextReader will be encoded into bytes via the UTF-8 character encoding
before being passed to Mosel; conversely, the text to be written to a TextWriter will have been
produced by decoding the Mosel output which is assumed to be in UTF-8. If this is not the desired
result, consider using a Stream instead. If you wish data exchange to be performed in a different
encoding (such as the platform’s default encoding), this can be done Mosel by use of the enc I/O driver(see C.7); only Streams, not TextReaders or TextWriters, are suitable for this.
using System;
using System.IO;
using System.Text;
using Mosel;
namespace ugcb.cs {public class ugcb {/// <summary>/// Main entry point for the application/// </summary>[STAThread]static void Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Associate .NET object with a name in Mosel
mosel.Bind("mycb", new MyOut());// Redirect error stream to stdout
mosel.SetDefaultStream(XPRMStreamType.F_ERROR, Console.Out);// Compile and load the Mosel model
XPRMModel model = mosel.CompileAndLoad("burglar2.mos");// Redirect the model's output to a custom TextWriter
MyOut modelOut = new MyOut();
model.SetDefaultStream(XPRMStreamType.F_OUTPUT_LINEBUF, modelOut);// Alternative:// Redirect the model's output to our printing function 'cbmsg'
model.SetDefaultStream(XPRMStreamType.F_OUTPUT_LINEBUF, "dotnet:mycb");// Run the model
model.Run();}}public class MyOut: TextWriter{private bool atStartOfLine = true;
public override void Write(char b){
if (atStartOfLine) {Console.Write("Mosel: ");atStartOfLine=false;}
if (b=='\n') {Console.WriteLine();atStartOfLine=true;}else if (b=='\r') {// ignore}
else {
Console.Write(b);Fair Isaac Corporation Conﬁdential and Proprietary Information

142

Other programming language interfaces

}

14.3

}
}
public override Encoding Encoding {get {return Encoding.UTF8;
}
}
}

VBA
VBA typically serves for embedding a Mosel model into an Excel spreadsheet. In this section we shall
only show the parts relevant to the Mosel functions, assuming that the execution of a model is triggedby the action of clicking on some object such as the buttons shown in Figure 14.1.

Figure 14.1: Excel spreadsheet embedding VBA macros

14.3.1

Compiling and executing a model in VBA
As with the other programming languages, to execute a Mosel model in VBA we need to perform thestandard compile/load/run sequence as shown in the following example (contained in the ﬁleugvb.bas). We use a slightly modiﬁed version burglar5.mos of the burglar problem where we haveredirected the output printing to the ﬁle burglar_out.txt.
Private Sub burglar_Click()Dim model
Dim ret As Long
Dim result As Long
Dim outfile As String, moselfile As String'Initialize Mosel
ret = XPRMinit
If ret Then
MsgBox "Initialization error (" & ret & ")"Exit SubEnd If'Compile burglar5.mos
XPRMsetdefworkdir GetFullPath()
moselfile = GetFullPath() & "\" & "burglar5"
outfile = GetFullPath() & "\" & "burglar_out.txt"
ret = XPRMcompmod(vbNullString, moselfile & ".mos", vbNullString, _Fair Isaac Corporation Conﬁdential and Proprietary Information

143

Other programming language interfaces"Burglar problem")If ret <> 0 Then
MsgBox "Compile error (" & ret & ")"Exit SubEnd If'Load burglar5.bim
model = XPRMloadmod(moselfile & ".bim", vbNullString)If model = 0 Then
MsgBox "Error loading model"
Exit SubEnd If'Run the model
ret = XPRMrunmod(model, result, "OUTFILE=""" & Replace(outfile, "\", _"\\") & """")
If ret <> 0 Then
MsgBox "Execution error (" & ret & ")"GoTo done
Else
ShowFile outfile
End If
MsgBox vbNewLine & "model Burglar returned: " & result
done:
XPRMfree
End Sub'Auxiliary routines
Private Sub ShowFile(fn As String)Dim vs As Stringvs = CreateObject("Scripting.FileSystemObject").OpenTextFile(fn).ReadAll
MsgBox vs
End Sub
Private Function GetFullPath() As String
Dim path As String
path = ThisWorkbook.path
If Right(path, 1) = "\" Then path = Left(path, Len(path) - 1)
GetFullPath = path
End Function

This implementation redirects the output into a log ﬁle vbout.txt the contents of which is displayed
after a successful model run.

14.3.2

Parameters
When executing a Mosel model in VBA, it is possible to pass new values for its parameters into themodel. The following program (also contained in the ﬁle ugvb.frm) extract shows how we may runmodel ‘Prime’ from Section 8.3 to obtain all prime numbers up to 500 (instead of the default value 100
set for the parameter LIMIT in the model). We use a slightly modiﬁed version prime4.mos of themodel where we have redirected the output printing to a ﬁle denoted by the parameter OUTFILE.
Private Sub prime_Click()Dim model
Dim ret As Long
Dim result As Long
Dim outfile As String, moselfile As String'Initialize Mosel
ret = XPRMinit
If ret Then
MsgBox "Initialization error (" & ret & ")"Exit SubEnd IfFair Isaac Corporation Conﬁdential and Proprietary Information

144

Other programming language interfaces'Compile prime4.mos
XPRMsetdefworkdir GetFullPath()
moselfile = GetFullPath() & "\" & "prime4"
outfile = GetFullPath() & "\" & "vbout.txt"
ret = XPRMcompmod(vbNullString, moselfile & ".mos", vbNullString, _"Prime numbers")
If ret <> 0 Then
MsgBox "Compile error (" & ret & ")"Exit SubEnd If'Load prime4.bim
model = XPRMloadmod("prime4.bim", vbNullString)If model = 0 Then
MsgBox "Error loading model"
Exit SubEnd If'Run model with new parameter settings
ret = XPRMrunmod(model, result, "LIMIT=500,OUTFILE=""" & Replace(outfile, _"\", "\\") & """")
If ret <> 0 Then
MsgBox "Execution error (" & ret & ")"GoTo done
Else
ShowFile outfile
End If
MsgBox vbNewLine & "model Prime returned: " & result
done:
XPRMfree
End Sub

14.3.3

Redirecting the Mosel output
In the previous example we have hardcorded the redirection of the output directly in the model. With
Mosel’s VBA interface the user may also redirect all output produced by Mosel to ﬁles directly from thehost application, that is, redirect the output stream.
To redirect all output of a model to the ﬁle myout.txt add the following function call before theexecution of the Mosel model:' Redirect all output to the file "myout.txt"
XPRMsetdefstream 0, XPRM_F_OUTPUT, "myout.txt"

Similarly, any possible error messages produced by Mosel can be recovered by replacing in the line
above XPRM_F_OUTPUT by XPRM_F_ERROR. This will redirect the error stream to the ﬁle myout.txt.
The following VBA program extract (ﬁle ugcb.bas) shows how to use a callback in VBA to receive all
output from a Mosel model (standard output and errors). The output will be displayed in the
spreadsheet from where the model run was started.
Private ROWNUM As Long
Public Sub example()
Dim ret As Long
Dim result As Long
Dim module
ClearColumn' Initialize Mosel. Must be called first
ret = XPRMinit
If ret <> 0 Then

Fair Isaac Corporation Conﬁdential and Proprietary Information

145

Other programming language interfaces
PrintLn ("Failed to initialize Mosel")
Exit SubEnd If' Redirect the output and error streams to the callback
ret = XPRMsetdefstream(0, XPRM_F_OUTPUT, XPRM_IO_CB(AddressOf OutputCB))
ret = XPRMsetdefstream(0, XPRM_F_ERROR, XPRM_IO_CB(AddressOf OutputCB))
PrintLn "Starting model..."' Run the model
ret = XPRMexecmod("", GetFullPath() & "\" & "burglar10.mos", _"FULLPATH='" & GetFullPath() & "'", result, module)
If ret <> 0 Then
PrintLn ("Failed to execute model")GoTo done
Else
PrintLn "Finished model"
End Ifdone:
XPRMfree
End Sub#If VBA7 Then
Private Sub OutputCB(ByVal model As LongPtr, ByVal ref As LongPtr, _ByVal msg As String, ByVal size As Long)' Output to the spreadsheet
Call PrintLn(msg)
End Sub#Else
Private Sub OutputCB(ByVal model As Long, ByVal ref As Long, _ByVal msg As String, ByVal size As Long)' Output to the spreadsheet
Call PrintLn(msg)
End Sub#End If
Public Sub PrintLn(ByVal msg As String)' Strip any trailing newlines first
If Right(msg, Len(vbLf)) = vbLf Then msg = Left(msg, Len(msg) - Len(vbLf))
If Right(msg, Len(vbCr)) = vbCr Then msg = Left(msg, Len(msg) - Len(vbCr))
Worksheets("Run Model").Cells(ROWNUM, 2) = Trim(msg)
ROWNUM = ROWNUM + 1
End Sub
Sub ClearColumn()
Worksheets("Run Model").Columns(2).ClearContents
ROWNUM = 1
End Sub
Function GetFullPath() As String
Dim path As String
path = ThisWorkbook.path
If Right(path, 1) = "\" Then path = Left(path, Len(path) - 1)
GetFullPath = path
End Function

Fair Isaac Corporation Conﬁdential and Proprietary Information

146

IV. Extensions and tools

Overview
Beyond what one might call the ‘standard use’ of Mosel, the Mosel environment has an increasing
number of advanced features, some of which you might ﬁnd helpful for the development or deploymentof larger applications.
The ﬁrst chapter of this part (Chapter 15) introduces the Mosel Debugger and Proﬁler, two tools that are
particularly helpful for the development and analysis of large-scale Mosel models. We give some hints
how you might improve the efﬁciency of your models.
The next chapter (Chapter 16) introduces the notion of packages and shows several examples of theiruse. It also discusses the differences between packages and modules and their respective uses.
Chapter 17 gives an overview of other advanced functionality, including generalized ﬁle handling,concurrency in modeling, graphing, and other solver types. In depth introductions to these topics are
given in separate manuals or whitepapers to avoid overloading this user guide.
The last chapter introduces the notion of ’annotations’ and tools of the Mosel distribution that exploit
such metadata, for example in the automated generation of Mosel model documentation.

Fair Isaac Corporation Conﬁdential and Proprietary Information

148

CHAPTER 15

Debugger and Proﬁler
15.1

The Mosel Debugger
In Chapter 6 we have seen how the Mosel Parser helps detect syntax errors during compilation. Other
types of errors that are in general more difﬁcult to analyze are mistakes in the data or logical errors in
the formulation of Mosel models. The Mosel Debugger may help tracing these.

15.1.1

Using the Mosel Debugger
In this section we shall be working with the model prime2.mos. This is the same model for calculating
prime numbers as the example we have seen in Section 8.3, but with a LIMIT value set to 20,000.
Mosel models that are to be run in the debugger need to be compiled with the option G. The Mosel
debugger is started with the command debug (it will automatically compile with the required settings):
mosel debug prime.mosand terminated by typing quit. Just as for the run command the user may specify new settings forthe model parameters immediately following the debug command:
mosel debug prime2.mos 'LIMIT=50'

Once the debugger is started, type in the following sequence of commands (Mosel’s output ishighlighted in bold face):

Fair Isaac Corporation Conﬁdential and Proprietary Information

149

Debugger and Proﬁlerdbg>break 31
Breakpoint 1-1 set at prime2.mos:31
dbg>bcond 1-1 getsize(SNumbers) <10dbg>cont
Prime numbers between 2 and 50:
Breakpoint 1-1.
31 while (not(n in SNumbers)) n+=1
dbg>print n
13dbg>display SNumbers
1(0): SNumbers = [17 19 23 29 31 37 41 43 47]
dbg>display SPrime2(0): SPrime = [2 3 5 7 11 13]
dbg>cont
Breakpoint 1-1.
31 while (not(n in SNumbers)) n+=1
1(0): SNumbers = [19 23 29 31 37 41 43 47]
2(0): SPrime = [2 3 5 7 11 13 17]dbg>cont
Breakpoint 1-1.
31 while (not(n in SNumbers)) n+=1
1(0): SNumbers = [23 29 31 37 41 43 47]
2(0): SPrime = [2 3 5 7 11 13 17 19]
dbg>cont
Breakpoint 1-1.
31 while (not(n in SNumbers)) n+=1
1(0): SNumbers = [29 31 37 41 43 47]
2(0): SPrime = [2 3 5 7 11 13 17 19 23]
dbg>quit

This small example uses many of the standard debugging commands (for a complete list, includingcommands for navigating in the Mosel stack that are not shown here, please see the Section ’Running
Mosel – Command line interpreter: debugger’ of the introduction chapter of the Mosel Language
Reference Manual):
break

Set a breakpoint in the given line. A breakpoint is deleted with delete followed by thebreakpoint number. The command breakpoints lists all currently deﬁned breakpoints.

bcond

Set a condition on a breakpoint (using the number of the breakpoint returned by thebreak command). Conditions are logical expressions formed according to thestandard rules in Mosel (use of brackets, connectors and and or). They may containany of the functions listed below.

cont

Continue the execution up to the next breakpoint (or to the end of the program). A
line-wise evaluation is possible by using next or step (the former jumps over loops
and subroutines, the latter steps into them).

displayShow the current value of a model object or an expression at every step of the debugger.
A display is removed by calling undisplay followed by the number of the display.

print

Show (once) the current value of a model object.

The following simple Mosel functions may be used with debugger commands (in conditions or withprint / display):

Arithmetic functions: abs, ceil, floor, round

Accessing solution values: getsol, getdual, getrcost, getactivity, getslack

Other: getparam, getsize

Fair Isaac Corporation Conﬁdential and Proprietary Information

150

Debugger and Proﬁler

15.1.1.1

Debugging concurrent models

The Mosel debugger can be used with concurrent (sub)models. A few temporary edits to the modelﬁles may be necessary in this case (to be removed for production versions!):

We need to use the compilation ﬂag ’G’ with all models that are to be debugged: this option isapplied automatically by the Mosel debugger for the master model, but we need to use it explicitly
for the submodels. For instance, if the submodels are compiled from the master model, we needto modify the compilation statement to include this ﬂag:
if compile("G","prime2d.mos")<>0 then exit(1); end-if



Breakpoints on submodels can only be set once the corresponding submodels have been started.
If their execution is too fast to allow for user input in the debugger, we recommend to insert a’sleep’ at the start of the submodel (this subroutine is provided by the module mmsystem thatneeds to be loaded by the model) during the debugging phase. For example adding a ’sleep’ of 5
seconds:
sleep(5000)

We can now start the debugger for the master model with a command like the following:mosel debug runprime2d.mos 'LIMIT=50'

Once the debugger is started, type in the following sequence of commands (Mosel’s output ishighlighted in bold face):dbg>break 29
Breakpoint 1-1 set at runprime2d.mos:29
dbg>cont
29 run(modPrime, "LIMIT="+LIMIT)
dbg>next
30 wait[model #2 starting]dbg>model 2⁎ "prime2d.mos":18
18 LIMIT=20000
dbg>break 34
Breakpoint 2-1 set at prime2d.mos:34
dbg>bcond 2-1 getsize(SNumbers) <10dbg>cont
Prime numbers between 2 and 50:
Breakpoint 2-1.
34 while (not(n in SNumbers)) n+=1
dbg>print n
13dbg>display SNumbers
1(#1): SNumbers = [17 19 23 29 31 37 41 43 47]
dbg>display SPrime
2(#1): SPrime = [2 3 5 7 11 13]
dbg>cont
Breakpoint 2-1.
34 while (not(n in SNumbers)) n+=1
1(#1): SNumbers = [19 23 29 31 37 41 43 47]
2(#1): SPrime = [2 3 5 7 11 13 17]dbg>cont
Breakpoint 2-1.
...
dbg>quit

This sequence sets a breakpoint on the submodel ’run’ command in the master model. After thesubmodel is started it switches to using the submodel (’model 2’) as the active model in the debugger;

Fair Isaac Corporation Conﬁdential and Proprietary Information

151

Debugger and Proﬁler

we can then enter the debug commands for the submodel. Notice that the command cont applies
globally to all running models, whereas next or step refer to the selected active model.

15.1.2

Debugger in Xpress WorkbenchWith Xpress Workbench the debugger is started by clicking on the button. Workbench willautomatically recompile the model with the required debugging ﬂag. Breakpoints are set by clicking
onto the gray area (left to the line number if it is displayed) preceding each row in the editor window,
breakpoint conditions can be added via the right mouse button menu on the breakpoint icon. Clicking
on the breakpoint icon deletes the breakpoint.
Delete breakpoint/deactivated breakpoint.
Delete a conditional breakpoint/deactivated conditional breakpoint.
Navigating in the debugger is possible by clicking on the corresponding buttons:
Activate/deactivate all breakpoints.
Start/stop the debugger.
Resume/suspend model execution.
Step over an expression.
Step into an expression.
Step out of an expression.
During a debugging session, the current position is indicated via a green arrow left to the line numbers(changing to yellow on breakpoints). Expand the Variables display in the Debugger pane on theright side of the workspace to observe the values of model entities.

15.2

Efﬁcient modeling through the Mosel Proﬁler
The efﬁciency of a model may be measured through its execution speed and also its memory
consumption. The execution times can be analyzed in detail with the help of the Mosel Proﬁler. Several
commands of the Mosel debugger that are also discussed in this section provide the user with further
information, such as memory consumption.

15.2.1

Using the Mosel Proﬁler
Once a model you are developing is running correctly, you will certainly start testing it with larger data
sets. Doing so, you may ﬁnd that model execution times become increasingly larger. This may be dueto the solution algorithms, but a more or less signiﬁcant part of the time will be spent simply indeﬁning the model itself. The Mosel Proﬁler lets you analyze the model behavior line-by-line. Solution
algorithms, such as LP or MIP optimization with Xpress Optimizer, may be improved by tuning solver
parameters (please refer to the corresponding software manuals). Here we shall be solely concernedwith improvements that may be made directly to the Mosel model. Even for large scale models, model
execution times can often be reduced to just a few seconds by carefully (re)formulating the model.
Just as for the debugger, Mosel models that are to be run in the proﬁler need to be compiled with theoption G. The command profile of the Mosel command line performs all required steps:
mosel profile prime2.mos

Fair Isaac Corporation Conﬁdential and Proprietary Information

152

Debugger and Proﬁler

or, if we wish to generate the BIM ﬁle explicitly:
mosel comp -G prime2.mos
mosel run -prof prime2.bim

The proﬁler generates a ﬁle filename.prof with the proﬁling statistics. For the test model
prime2.mos this ﬁle has the following contents (leaving out the header lines and comments):
model Prime
1

0.00

0.00

parameters
LIMIT=20000
end-parameters

1
1

0.00
0.00

0.00
0.00

declarations
SNumbers: set of integer
SPrime: set of integer
end-declarations

1

0.01

0.00

SNumbers:={2..LIMIT}

1

0.00

0.01

writeln("Prime numbers between 2 and ", LIMIT, ":")

1
1
2262
2262
2262
2262
50126
50126

0.00
0.00
0.04
0.00
0.00
0.04
3.31
0.04

0.01
0.01
3.44
3.44
3.44
3.44
3.44
3.44

2262

0.00

3.44

n:=2
repeatwhile (not(n in SNumbers)) n+=1
SPrime += {n}i:=nwhile (i<=LIMIT) do
SNumbers-= {i}i+=n
end-dountil SNumbers={}

1
1

0.00
0.00

3.44
3.45

writeln(SPrime)
writeln(" (", getsize(SPrime), " prime numbers.)")

1

0.00

3.45 end-model

The ﬁrst column lists the number of times a statement is executed, the second column the total timespent in a statement, and the third column the time of the last execution; then follows thecorresponding model statement. In our example, we see that most of the model execution time is spentin a single line, namely the deletion of elements from the set SNumbers. This line is executed more than
50,000 times, but so is the following statement (i+=n) and it only takes a fraction of a second. Indeed,
operations on large (>1000 entries) sets may be relatively expensive in terms of running time. If ourprime number algorithm were to be used in a large, time-critical application we should give preference
to a more suitable data structure that is addressed more easily, that is, an array. For instance, bymodifying the model as follows the total execution time for this model version becomes 0.19 seconds:
model "Prime (array)"parameters
LIMIT=20000
end-parameters! Search for prime numbers in 2..LIMIT

declarations
INumbers = 2..LIMIT! Set of numbers to be checked
SNumbers: array(INumbers) of boolean
SPrime: set of integer! Set of prime numbers
end-declarations
writeln("Prime numbers between 2 and ", LIMIT, ":")
n:=2

Fair Isaac Corporation Conﬁdential and Proprietary Information

153

Debugger and Proﬁler
repeatSPrime += {n}! n is a prime number
i:=nwhile (i<=LIMIT) do! Remove n and all its multiples
SNumbers(i):= truei+=n
end-dowhile (n <= LIMIT and SNumbers(n)) n+=1
until (n>LIMIT)
writeln(SPrime)
writeln(" (", getsize(SPrime), " prime numbers.)")end-model

To start the Mosel proﬁler from Xpress Workbench, open the Run Dialog window from the menu Run orand select the run mode Proﬁle the Model.
by clicking on the tools button

15.2.1.1

Proﬁling concurrent models

The Mosel proﬁler can be used to proﬁle models that are running concurrently. The proﬁler run isstarted by launching the proﬁler for the master model. For every model ﬁle, an output ﬁlefilename.prof is generated. If several instances of the same model ﬁle are being run, Mosel creates
unique ﬁlenames of the form filename.modelid.prof where modelid is formed from the model
counter per model tree level.
The user is reminded that all (sub)models used in proﬁler runs need to be compiled with the ’G’ ﬂag.

15.2.2Other commands for model analysis
The Mosel debugger provides a few other commands that may be helpful with quickly obtaining
information about models that have been executed in Mosel.
Consider, for example, the following model flow.mos.
model "Dynamic arrays"
declarations
Suppliers = 1..150
Customers = 1..10000
COST: dynamic array(Suppliers,Customers) of real
flow: dynamic array(Suppliers,Customers) of mpvar
end-declarations
initializations from "flow.dat"
COST
end-initializations
forall(s in Suppliers, c in Customers | COST(s,c)>0 ) create(flow(s,c))
end-model

Now execute the following sequence of Mosel commands from the command line (as before, Mosel
output is printed in bold face). The commands we wish to use are part of the Mosel debugger.—Since
we do not wish to launch a debugging session, we use the option -g to compile in debug mode, but
without tracing information. This results in a standard model run without entering an interactive
debugging session.

Fair Isaac Corporation Conﬁdential and Proprietary Information

154

Debugger and Proﬁler
mosel debug -g flow.mosdbg>lsmods⁎ name: Dynamic arrays (0.0.0) number: 1 size: 47884
sys. com.: ‘flow.mos’,debug,mc5.0.0
user com.:dbg>info COST
‘COST’ is an array (dynamic, dim: 2, size: 750) of realsdbg>quit

The command lsmods displays information about all models loaded in Mosel, and in particular theirsize (= memory usage in bytes). With the command info followed by a symbol name we obtain
detailed information about the deﬁnition of this symbol (without giving a symbol this command will
display release and license information for Mosel). Alternatively, it is also possible to print the complete
list of symbols (with type information and sizes) deﬁned by the current model by using the command
lssymb.
If we now remove the keyword dynamic from the declaration of the two arrays, COST and flow, andre-run the same command sequence as before, we obtain the following output:dbg>lsmods⁎ name: Dynamic arrays number: 1 size: 36011152
Sys. com.: ‘flow.mos’,debug,mc5.0.0
User com.:dbg>info COST
‘COST’ is an array (dim: 2, size: 1500000) of reals

We can run a similar experiment with the model version flowh.mos that deﬁnes the two sparse arrays
as hashmap. As shown in the output below, the memory usage is somewhat higher albeit in the sameorder of magnitude as the model version with dynamic arrays:
mosel debug -g flowh.mosdbg>lsmods⁎ name: Hashmap arrays (0.0.0) number: 1 size: 81488
sys. com.: ‘flowh.mos’,debug,mc5.0.0
user com.:dbg>info COST
‘COST’ is an array (hashmap, dim: 2, size: 750) of realsdbg>quit

It is easily seen that in this model the use of the keyword dynamic or hashmap makes a huge
difference in terms of memory usage. A model deﬁning several arrays of comparable sizes is likely to
run out of memory (or at the least, it may not leave enough memory for an optimization algorithm to be
executed).
Note: If COST is deﬁned as a sparse (dynamic or hashmap) array, the condition on the forall loop
should really be exists(COST(s,c)) for speedier execution of this loop.

15.2.3

Some recommendations for efﬁcient modeling
The following list summarizes some crucial points to be taken into account, especially when writing
large-scale models. For more details and examples please see Appendix B.
Use sparse arrays to– size data tables automatically when the data is read in,– initialize the index values automatically when the data is read in,– conserve memory when storing sparse data,

Fair Isaac Corporation Conﬁdential and Proprietary Information

155

Debugger and Proﬁler– eliminate index combinations without using conditions each time.
Don’t use sparse arrays– when you can use ordinary (dense) arrays instead,– when storing dense data (if at least 50% of its entries are deﬁned an array should clearly beconsidered as dense), and you can size the data table and initialize the indices in some otherway, such as by reading in the size ﬁrst.

General procedure for declaring and initializing data:
1. declare all index sets and the minimal collection of data arrays required to initialize the sets,
2. initialize the data arrays (which also initializes all index sets),
3. ﬁnalize the index sets,
4. declare and initialize all other arrays.



Efﬁcient use of sparse arrays:– use the keyword exists for enumeration (in sums or loops),– access the elements in ascending order of the indices (particularly with dynamic arrays),– use hashmap when array elements are predominantly accessed in random order,– use ranges, rather than sets, for the index sets.



Efﬁcient use of exists:– use named index sets in the declarations,– use the same index sets in the loops,– use the index sets in the same order,– use the dynamic/hashmap qualiﬁer if some index sets are constant or ﬁnalized,– make sure exists is the ﬁrst condition,– always use exists, even if no condition or an alternative condition is logically correct,– conditions with or cannot be handled as efﬁciently as conditions with and.



Loops (sum, forall, etc.):– where possible, use conditional loops—loop index set followed by a vertical bar and thecondition(s)—instead of a logical test with if within the loop,– make sure exists is the ﬁrst condition,– always use exists, even if no condition or an alternative condition is logically correct,– enumerate the index sets in the same order as they occur in the arrays within the loop,– broken up, conditional loops are handled less efﬁciently.



Do not use any debugging ﬂag for compiling the ﬁnal deployment version of your models.

Fair Isaac Corporation Conﬁdential and Proprietary Information

156

CHAPTER 16

Packages
A package is a library written in the Mosel language (this feature is introduced by Mosel 2.0). Its
structure is similar to models, replacing the keyword model by package. Packages are included intomodels with the uses statement for dynamic loading (the package BIM needs to be present for model
execution), in the same way as this is the case for modules (DSO). Alternatively, packages can beloaded statically via imports in which case they get included in the model BIM ﬁle (this option is notavailable for modules that are always dynamic). Unlike Mosel code that is included into a model with
the include statement, packages are compiled separately, that is, their contents are not visible to theuser.
Typical uses of packages include

development of your personal ‘tool box’model parts (e.g., reformulations) or algorithms written in Mosel that you wish to distributewithout disclosing their contents
add-ons to modules that are more easily written in the Mosel language

Packages may deﬁne new constants, subroutines, types, and parameters for the Mosel language asshown in the following examples (the ﬁrst two examples correspond to the ﬁrst two module examplesof the Mosel Native Interface User Guide).

16.1

Deﬁnition of constants
The following package myconstants deﬁnes one integer, one real, one string, and two boolean
constants.
package myconstants
public declarations
MYCST_BIGM = 10000! A large integer value
MYCST_TOL = 0.00001! A tolerance value
MYCST_LINE =! String constant"----------------------------------------------------------------"
MYCST_FLAG = true
! Constant with value true
MYCST_NOFLAG = false! Constant with value false
end-declarations
end-package

The structure of a package is similar to the structure of Mosel models, with the difference that we use
the keyword package instead of model to mark its beginning and end.
After compiling our package with the standard Mosel command (assuming the package is saved in ﬁlemyconstants.mos)

Fair Isaac Corporation Conﬁdential and Proprietary Information

157

Packages
mosel comp myconstants

it can be used in a Mosel model (ﬁle myconst_test.mos):model "Test myconstants package"
uses "myconstants"
writeln(MYCST_LINE)
writeln("BigM value: ", MYCST_BIGM, ", tolerance value: ", MYCST_TOL)
writeln("Boolean flags: ", MYCST_FLAG, " ", MYCST_NOFLAG)
writeln(MYCST_LINE)
end-model

Please note the following:
1. Package name: compiling a package will result in a ﬁle packagename.bim. This package isinvoked in a Mosel model by the statement
uses "packagename"
The name of the Mosel package source ﬁle (.mos ﬁle) may be different from the name given to
the BIM ﬁle.
2. Internal package name: the name given in the Mosel ﬁle after the keyword package is theinternal name of the package. It must be a valid Mosel identiﬁer (and not a string). This namemay be different from the name given to the BIM ﬁle, but it seems convenient to use the same
name for both.
3. Package location: for locating packages Mosel applies the same rules as for locating modules; itﬁrst searches in the directory dso of the Xpress installation, that is, in XPRESSDIR/dso, and thenin the directories pointed to by the environment variable MOSEL_DSO. The contents of the lattercan be set freely by the user.
To try out the package examples in this chapter, you may simply include the current working
directory (’.’) in the locations pointed to by MOSEL_DSO, so that packages in the current
working directory will be found, for example:
Windows: set MOSEL_DSO=.
Unix/Linux, C shell: setenv MOSEL_DSO .
Unix/Linux, Bourne shell: export MOSEL_DSO; MOSEL_DSO=.
Alternatively, you can use the compilation option -bx to indicate the location of package ﬁles (thisoption does not apply to DSOs):
mosel exe -bx ./ mymodel.mos
In general, and in particular for the deployment of an application, it is recommended to work withabsolute paths in the deﬁnition of environment variables.
Having made sure that Mosel is able to ﬁnd our package myconstants.bim, executing the test model
above will produce the following output:---------------------------------------------------------------BigM value: 10000, tolerance value: 1e-05
Boolean flags: true false----------------------------------------------------------------

When comparing with the C implementation of the module example myconstants in the Mosel Native
Interface User Guide we can easily see that the package version is much shorter.

16.2

Deﬁnition of subroutines
We now show a package (ﬁle solarraypkg.mos) that deﬁnes several versions of a subroutine,

Fair Isaac Corporation Conﬁdential and Proprietary Information

158

Packages

solarray, which copies the solution values of an array of decision variables of type mpvar into anarray of real of the same size. For each desired number (1–3) and type (integer or string) of arrayindices we need to deﬁne a new version of this subroutine.
package solarraypkg! ⁎⁎⁎⁎ Integer indices (including ranges) ⁎⁎⁎⁎
public procedure solarray(x:array(R:set of integer) of mpvar,s:array(set of integer) of real)
forall(i in R) s(i):=getsol(x(i))
end-procedurepublic procedure solarray(x:array(R1:set of integer,
R2:set of integer) of mpvar,s:array(set of integer,
set of integer) of real)
forall(i in R1, j in R2) s(i,j):=getsol(x(i,j))
end-procedurepublic procedure solarray(x:array(R1:set of integer,
R2:set of integer,
R3:set of integer) of mpvar,s:array(set of integer,
set of integer,
set of integer) of real)
forall(i in R1, j in R2, k in R3) s(i,j,k):=getsol(x(i,j,k))
end-procedure! ⁎⁎⁎⁎String indices ⁎⁎⁎⁎
public procedure solarray(x:array(R:set of string) of mpvar,s:array(set of string) of real)
forall(i in R) s(i):=getsol(x(i))
end-procedurepublic procedure solarray(x:array(R1:set of string,R2:set of string) of mpvar,s:array(set of string,
set of string) of real)
forall(i in R1, j in R2) s(i,j):=getsol(x(i,j))
end-procedurepublic procedure solarray(x:array(R1:set of string,R2:set of string,R3:set of string) of mpvar,s:array(set of string,
set of string,
set of string) of real)
forall(i in R1, j in R2, k in R3) s(i,j,k):=getsol(x(i,j,k))
end-procedure
end-package

Using the package in a Mosel model (ﬁle solarr_test.mos):model "Test solarray package"
uses "solarraypkg", "mmxprs"
declarations
R1=1..2
R2={6,7,9}
R3={5,-1}
x: array(R1,R2,R3) of mpvar
sol: array(R1,R2,R3) of real
end-declarations! Define and solve a small problem
sum(i in R1, j in R2, k in R3) (i+j+2⁎k) ⁎ x(i,j,k) <= 20
forall(i in R1, j in R2, k in R3) x(i,j,k)<=1

Fair Isaac Corporation Conﬁdential and Proprietary Information

159

Packages
maximize(sum(i in R1, j in R2, k in R3) (i+2⁎j+k) ⁎ x(i,j,k))! Get the solution arraysolarray(x,sol)! Print the solution
forall(i in R1, j in R2, k in R3)
writeln(" (", i, ",", j, ",", k, ") ", sol(i,j,k), " ", getsol(x(i,j,k)))writeln(sol)end-model

Output produced by this model:(1,6,-1) 1 1(1,6,5) 0 0(1,7,-1) 1 1(1,7,5) 0 0(1,9,-1) 1 1(1,9,5) 0 0(2,6,-1) 0.166667 0.166667(2,6,5) 0 0(2,7,-1) 0 0(2,7,5) 0 0(2,9,-1) 0 0(2,9,5) 0 0[1,0,1,0,1,0,0.166667,0,0,0,0,0]This example may be classiﬁed as a ‘utility function’ that eases tasks occurring in a similar way inseveral of your models. Another example of such a utility function could be a printing function that
simply outputs the solution value of a decision variable with some ﬁxed format (if you apply
write/writeln to a decision variable of type mpvar you obtain the pointer of the variable, and not its
solution value).
If we again make the comparison with the implementation as a module we see that both ways havetheir positive and negative points: the implementation as a module is clearly more technical, requiring aconsiderable amount of C code not immediately related to the implementation of the function itself.
However, at the C level we simply check that the two arguments have the same index sets, without
having to provide a separate implementation for every case, thus making the deﬁnition more general.

16.3

Deﬁnition of types
In Section 8.6.2 we have seen the example arcs.mos that deﬁnes a record to represent arcs of a
network. If we wish to use this data structure in different models we may move its deﬁnition into a
package ’arcpkg’ to avoid having to repeat it in every model.
Such a package may look as follows (ﬁle arcpkg.mos):package arcpkg
public declarationsarc = public record
Source,Sink: string
Cost: real
end-record
end-declarations! Arcs:!
Source and sink of arc!
Cost coefficient

end-package

which is used thus from the model ﬁle:Fair Isaac Corporation Conﬁdential and Proprietary Information

160

Packages
model "Arcs2"
uses "arcpkg"
declarations
NODES: set of string
ARC: array(ARCSET:range) of arc
end-declarations! Set of nodes! Arcsinitializations from 'arcs.dat'
ARC
end-initializations! Calculate the set of nodes
NODES:=union(a in ARCSET) {ARC(a).Source, ARC(a).Sink}
writeln(NODES)
writeln("Average arc cost: ", sum(a in ARCSET) ARC(a).Cost / getsize(ARCSET) )end-model

At this place, the use of the keyword public may call for some explanation. Here and also in theexample ‘myconstants’ the whole declarations block is preceded by the public marker, indicatingthat all objects declared in the block are public (i.e., usable outside of the package deﬁnition ﬁle). If only
some declarations are public and others in the same block are private to the package, the publicmarker needs to preceed the name of every object within the declarations that is to become publicinstead of marking the entire block as public.
The second occurrence of the public marker in the deﬁnition of package ‘arcpkg’ is immediately infront of the keyword record, meaning that all ﬁelds of the record are public. Again, it is possible toselect which ﬁelds are accessible from external ﬁles (for example, you may wish to reserve some ﬁelds
for special ﬂags or calculations within your package) by moving the keyword public from the record
deﬁnition in front of every ﬁeld name that is to be marked as public.
A deﬁnition of package ‘arcpkg’ equivalent to the one printed above therefore is the following.
package arcpkg2
declarations
public arc = record
public Source,Sink: string
public Cost: real
end-record
end-declarations! Arcs:!
Source and sink of arc!
Cost coefficient

end-package

16.4

Deﬁnition of parameters
Mosel parameters are scalars of one of the four basic types (real/integer/string/boolean). Packages
can deﬁne new parameters by declaring their names and type in the parameters section. The
package needs to store the current values of the parameters in separate model entities and will usually
initialize default values for the parameters.
package parpkg! Specify parameter names and types
parameters"p1":real"p2":integer"p3":string"p4":boolean
end-parameters

Fair Isaac Corporation Conﬁdential and Proprietary Information

161

Packages! Entities for storing current parameter values
declarations
myp1: real
myp2: integer
myp3: string
myp4: boolean
end-declarations! Set default values for parameters
myp1:=0.25
myp2:=10
myp3:="default"
myp4:=true!... Parameter access routines ...
end-package

The access routines for the four parameter types have a ﬁxed format, namely
packagename~get[r|i|s|b]param and packagename~set[r|i|s|b]param as shown in thecode extract below.
! Get value of a real parameter
public function parpkg~getrparam(p:string):realcase p of"p1": returned:=myp1
end-caseend-function! Get value of an integer parameter
public function parpkg~getiparam(p:string):integercase p of"p2": returned:=myp2
end-caseend-function! Get value of a string parameter
public function parpkg~getsparam(p:string):stringcase p of"p3": returned:=myp3
end-caseend-function! Get value of a boolean parameter
public function parpkg~getbparam(p:string):booleancase p of"p4": returned:=myp4
end-caseend-function! Set value for real parameters
public procedure parpkg~setparam(p:string,v:real)case p of"p1": myp1:=v
end-case
end-procedure! Set value for integer parameters
public procedure parpkg~setparam(p:string,v:integer)case p of"p2": myp2:=v
end-case
end-procedure! Set value for string parameters
public procedure parpkg~setparam(p:string,v:string)case p of"p3": myp3:=v

Fair Isaac Corporation Conﬁdential and Proprietary Information

162

Packages
end-case
end-procedure! Set value procedure for boolean parameters
public procedure parpkg~setparam(p:string,v:boolean)case p of"p4": myp4:=v
end-case
end-procedure

A model using the package ’parpkg’ will access the package parameters via Mosel’s standard
getparam and setparam routines ( the parameter names are not case-sensitive and their names canbe preceded by the package name).
model "Packages with parameters"
uses 'parpkg'! Display default parameter values
writeln("Default values:"," p1=", getparam("parpkg.P1"), " p2=", getparam("P2")," p3=", getparam("parpkg.p3"), " p4=", getparam("p4"))! Change values
setparam("p1",133)
setparam("parpkg.p2",-77)
setparam("P3","tluafed")
setparam("parpkg.P4",not getparam("parpkg.P4"))
end-model

16.5

Namespaces
A namespace is a group of identiﬁers in a program that is distinguished by a common name (preﬁx).
When working with mutiple packages it can be helpful to introduce namespaces in order to structure
the data and to determine which model entities are accessible to other (all or preselected) packages ormodels.
A fully qualiﬁed entity name in Mosel is of the form
nspc~ident

where nspc is a namespace name and ident an identiﬁer in the namespace. Namespaces and theiraccess are speciﬁed via speciﬁc compiler directives at the start of the model or package. The package
example mynspkg1 below deﬁnes three namespaces (’ns1’, ’ns3’, and ’ns3~ns31’), two of which are
restricted to a namespace group that comprises a second package mynspkg2, and the namespace’ns3’ is visible to all packages and models. The package further states via the nssearch directive thatany unqualiﬁed entity names employed in the package should be searched for in the namespace ’ns1’,
meaning that the names belonging to this namespace can be used without the namespace preﬁx ns1~.
package mynspkg1
namespace ns1, ns3, ns3~ns31
nsgroup ns1: "mynspkg2"
nsgroup ns3~ns31: "mynspkg2"
nssearch ns1
declarations
ns3~R = 1..10
ns1~Ar: array(ns3~R) of integervi, ns3~vi, ns3~ns31~vi: integer
end-declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information! This package defines 3 namespaces:! ⁎ ns1 + ns3~ns31 restricted to pkg2
! ⁎ ns3 is visible to all! 'ns1' can be used without prefix! Array with index set in another namespace! 3 different entities

163

Packages
public declarations
vp: integer
end-declarations! This entity is visible to allprocedure ns1~proc1(val:integer)! Subroutine in a namespacens3~vi:=val; ns3~ns31~vi:=2⁎val; vi:=val; vp:=val
Ar(5):=val! No prefix: 'ns1' is in search list
writeln(" In ns1~proc1: ", vi)
end-procedurepublic procedure proc2(val:integer)
writeln(" In proc2: ", val)
end-procedure! Public subroutine

procedure proc3(val:integer)
writeln(" In proc3: ", val)
end-procedure
end-package! Private subroutine

The package mynspkg1 shows some examples of entity and subroutine deﬁntions for the three cases:
private (vi, proc3), in a namespace (ns3~R, ns1~Ar, ns3~vi, ns3~ns31~vi, ns1~proc1), andpublic (vp, proc2).
The second package mynspkg2 that uses functionality from mynspkg1 needs to state whichnamespaces are used, either via a namespace or a nssearch directive.
package mynspkg2
uses 'mynspkg1'
namespace ns3~ns31
nssearch ns1! Namespace used in this package! 'ns1' can be used without prefix

public procedure frompkg2(val: integer)
proc1(val)! Procedure in namespace 'ns1'
writeln(" frompkg2:",ns3~ns31~vi) ! Namespace 'ns3~ns31' is not searchedwriteln(" vp=", vp)! Public symbol of pkg1
writeln(" Ar(5)=", Ar(5))! Contained in 'ns1' (prefix optional)
end-procedure
end-package

Any model or further package using the previous two packages can access the namespace ’ns3’ andalso deﬁne new namespaces of its own, but it is not allowed to access the other two namespaces thatare restricted to this group of packages.
model "mynstest"
uses 'mynspkg1', 'mynspkg2'
namespace ns2! A new namespace
nssearch ns3! Symbols from 'ns3' can be used without prefix
frompkg2(5)writeln("n3~vi:", vi, " vp:", vp)! Public routine from package mynspkg1! Display values of n3~vi and vp

proc2(4)! Public subroutine from mynspkg1

end-model

An interesting feature of namespaces is that an entire namespace can be saved via
initializations to simply by indicating its name and the stored information can subsequently be
used to initialize entities in some other namespace with matching names and types.
declarations
ns2~vi: integerI, ns2~R: range
end-declarations! Store contents of namespace 'ns3'

Fair Isaac Corporation Conﬁdential and Proprietary Information

164

Packages
initializations to "mem:mynsav"
ns3
end-initializations! Initialize entities with matching names from the saved namespace
initializations from "mem:mynsav"
ns2 as "ns3"
end-initializations
writeln("ns2~vi:", ns2~vi)! Has received the value of ns3~vi! Read an individual entity from the saved namespace
initializations from "mem:mynsav"
I as "ns3~R"
end-initializations
writeln("I:", I)

16.6

Packages vs. modules
The possibility of writing packages introduces a second form of libraries for Mosel, the ﬁrst being
modules (see the ‘Mosel Native Interface User Guide’ for further detail). The following list summarizes
the main differences between packages and modules.
Deﬁnition– Package* library written in the Mosel language– Module* dynamic library written in C that obeys the conventions of the Mosel Native Interface

Functionality– Package* deﬁne· symbols· subroutines· types· control parameters– Module* extend the Mosel language with· constant symbols· subroutines· operators· types· control parameters· I/O drivers



Efﬁciency– Package* like standard Mosel models– Module* faster execution speed

Fair Isaac Corporation Conﬁdential and Proprietary Information

165

Packages* higher development effort
Use– Package* making parts of Mosel models re-usable* deployment of Mosel code whilst protecting your intellectual property– Module* connection to external software* time-critical tasks* deﬁnition of new I/O drivers and operators for the Mosel language

As can be seen from the list above, the choice between packages and modules depends largely on thecontents and intended use of the library you wish to write.

Fair Isaac Corporation Conﬁdential and Proprietary Information

166

CHAPTER 17

Language extensions
It has been said before that the functionality of the Mosel language can be extended by means ofmodules, that is, dynamic libraries written in C/C++. All through this manual we have used the module
mmxprs to access Xpress Optimizer. Other modules we have used are mmsheet and mmodbc (accessto spreadsheets and databases, see Section 2.2.5), and mmsystem (Sections 5.1 and 11.1).
The full distribution of Mosel includes other functionality (modules and I/O drivers) that has not yet
been mentioned in this document. In the following sections we give an overview with links where to ﬁnd
additional information.

17.1

Generalized ﬁle handling
The notion of (data) ﬁle encountered repeatedly in this user guide seems to imply a physical ﬁle.
However, Mosel language statements (such as initializations from / to, fopen and fclose,
exportprob) and the Mosel library functions (e.g., XPRMcompmod, XPRMloadmod, or XPRMrunmod)
actually work with a much more general deﬁnition of a ﬁle, including (but not limited to)a physical ﬁle (text or binary)a block of memorya ﬁle descriptor provided by the operating systema function (callback)a database

The type of the ﬁle is indicated by adding to its name the name of the I/O driver that is to be used toaccess it. In Section 2.2.5 we have used mmodbc.odbc:blend.mdb to access an MS Accessdatabase via the ODBC driver provided by the module mmodbc. If we want to work with a ﬁle held inmemory we may write, for instance, mem:filename. The default driver (no driver preﬁx) is thestandard Mosel ﬁle handling.
More generally, an extended ﬁle name has the form driver_name:ﬁle_name or
module_name.driver_name:ﬁle_name if the driver is provided by the module module_name. The
structure of the ﬁle_name part of the extended ﬁle name is speciﬁc to the driver, it may also consist of
yet another extended ﬁle name (e.g. zlib.gzip:tmp:myfile.txt).

17.1.1

Displaying the available I/O drivers
The Mosel core drivers can be displayed from the command line with the following command (thelisting will also include any drivers that are provided by currently loaded modules):Fair Isaac Corporation Conﬁdential and Proprietary Information

167

Language extensions
mosel exam -i

The drivers provided by modules are displayed by the exam command for the corresponding module (inthis example: mmodbc)
mosel exam -i mmodbc

Library drivers (in particular the Java module mmjava that is embedded in the Mosel core and also the
mmdotnet module on Windows platforms) can be displayed with the help of the corresponding
program mmdispdso.[c|cs|java] in the subdirectory examples/mosel/Library of the Xpress
distribution. The command for running the Java version might look as follows (please refer to theprovided makefile):java -classpath $XPRESSDIR/xprm.jar:. mmdispdso mmjava

17.1.2

List of I/O drivers
The standard distribution of Mosel deﬁnes the following I/O drivers (tee, null, bin, and tmp are
documented in the ’Mosel Language Reference Manual’; the drivers sysfd, mem, cb, and raw thatmainly serve when interfacing Mosel with a host application are documented in the ‘Mosel Libraries
Reference Manual’):bin

Write (and read) data ﬁles in a platform independent binary format. The bin driver can
only be used for initializations blocks as a replacement of the default driver. Filesin bin format are generally smaller than the ASCII equivalent and preserve accuracy of
ﬂoating point numbers.
Example: the following outputs data to a text ﬁle in binary format
initializations to "bin:results.txt"

Another likely use of bin is in combination with mem or shmem for exchanging data inmemory (see Section 17.2.3):
initializations to "bin:shmem:results"cb

Use a (callback) function as a ﬁle (e.g., for reading and writing dynamically sized data ininitializations blocks, see the examples in Section 13.4.3, or to write your own
output or error stream handling functions when working with the Mosel libraries, see
Section 13.5 for an example).

mem

Use memory instead of physical ﬁles for reading or writing data (e.g., for exchanging
data between a model and the calling C application as shown in Section 13.4 or forcompiling/loading a model to/from memory when working with the Mosel libraries).
Example: the following lines will compile the Mosel model burglar2.mos to memory
and then load it from memory (full example in ﬁle ugcompmem.c).
XPRMmodel mod;char bimfile[2000];char bimfile_name[64];/⁎ Buffer to store BIM file ⁎//⁎ File name of BIM file ⁎/XPRMinit())/⁎ Initialize Mosel ⁎//⁎ Prepare file name for compilation using 'mem' driver: ⁎//⁎"mem:base_address/size[/actual_size_of_pointer]"⁎/sprintf(bimfile_name, "mem:%p/%d", bimfile, (int)sizeof(bimfile));/⁎ Compile model file to memory ⁎/XPRMcompmod(NULL, "burglar2.mos", bimfile_name, "Knapsack example"))Fair Isaac Corporation Conﬁdential and Proprietary Information

168

Language extensions/⁎ Load a BIM file from memory ⁎/mod = XPRMloadmod(bimfile_name, NULL);

null

Disable a stream.
Example: adding the line
fopen("null:", F_OUTPUT)

in a Mosel model will disable all subsequent output by this model (until the output
stream is closed or a new output stream is opened).
raw

Implementation of the initializations block in binary mode, typically used in
combination with mem for data exchange with a C host application (see Section 13.4).

sysfd

Working with operating system ﬁle descriptors (for instance, ﬁle descriptors returned by
the C function open).
Example: in a C program, the line
XPRMsetdefstream(NULL, XPRM_F_ERROR, "sysfd:1");

will redirect the Mosel error stream to the default output stream.
tee

Output into up to 6 ﬁles simultaneously (e.g., to display a log on screen and write it to a
ﬁle at the same time).
Example: adding the line
fopen("tee:result.txt&tmp:log.txt&", F_OUTPUT)

in a Mosel model will redirect all subsequent model output to the ﬁles result.txt andtmp:log.txt, and at the same time display the output on the default output (screen),
the latter is denoted by the & sign at the end of the ﬁlename string. The output to both
locations is terminated by the statement
fclose(F_OUTPUT)

after which output will again only go to default output.
tmp

Extension to the default driver that locates the speciﬁed ﬁle in the temporary directory
used by Mosel.
Example: adding the line
fopen("tmp:log.txt", F_OUTPUT+F_APPEND)

redirects all subsequent output from a models to the ﬁle log.txt that is located in
Mosel’s temporary directory. It is equivalent tofopen(getparam("TMPDIR") + "/log.txt", F_OUTPUT+F_APPEND)

Some modules, listed below in alphabetical order, deﬁne additional I/O drivers. The drivers are
documented with the corresponding module in the ‘Mosel Language Reference Manual’:

mmdotnet
dotnetUse a C# stream or object in place of a ﬁle in Mosel.
Example: The following C# program extract uses the dotnet driver to send datain standard initializations text format via a stream from the C# host program to amodel (the model ﬁle burglar13.mos is the same as in Section 13.4.3, it uses
the parameter DATAFILE as the ﬁlename for an initializations from blockthat expects to read data with the label ’DATA’.)Fair Isaac Corporation Conﬁdential and Proprietary Information

169

Language extensions// String containing initialization data for the model
const string BurglarDat ="DATA: [(\"camera\") [15 2] (\"necklace\") [100 20] " +"(\"vase\") [90 20] (\"picture\") [60 30] (\"tv\") [40 40] " +"(\"video\") [15 30] (\"chest\") [10 60] (\"brick\") [1 10] ]\n";static void Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load the Mosel model
XPRMModel model = mosel.CompileAndLoad("burglar13.mos");// Bind a stream based on the BurglarDat data to the name 'BurglarIni'// where the model will expect to find its initialization data
model.Bind("BurglarIni", new StringReader(BurglarDat));// Pass data location as a parameter to the model
model.SetExecParam("DATAFILE","dotnet:BurglarIni");}dotnetraw// Run the model
model.Run();Exchange of data between a Mosel model and the C# application running themodel using C# array structures; C# version of raw.
Example: Send the data held in the two arrays vdata and wdata to a Mosel andretrieve solution data into the array solution. We use the option noindex with thedotnetraw driver to indicate that all data are saved in dense format (i.e. as arrays
containing just the data values, without any information about the indices).
// Arrays containing initialization data for the model
static double[] vdata = new double[] {15,100,90,60,40,15,10, 1};
static double[] wdata = new double[] { 2, 20,20,30,40,30,60,10};// Main entry point for the application
static void Main(string[] args) {// Initialize Mosel
XPRM mosel = XPRM.Init();// Compile and load the Mosel model
XPRMModel model = mosel.CompileAndLoad("burglar8d.mos");// Associate the .NET objects with names in Mosel
model.Bind("vdat", vdata);
model.Bind("wdat", wdata);// Create a new array for solution data and bind it to the name 'sol'
double[] solution = new double[8];mosel.Bind("sol", solution);// Pass data location as a parameter to the model
model.ExecParams ="VDATA='noindex,vdat',WDATA='noindex,wdat',SOL='noindex,sol'";// Run the model
model.Run();}// Print the solution
Console.WriteLine("Objective value: {0}", model.ObjectiveValue);for (int i=0;i<8;i++)Console.Write(" take({0}): {1}", (i+1), solution[i]);Console.WriteLine();

The model ﬁle burglar8d.mos uses the driver name as the ﬁle name in theinitializations sections:

Fair Isaac Corporation Conﬁdential and Proprietary Information

170

Language extensions
initializations from 'dotnetraw:'
VALUE as VDATA WEIGHT as WDATA
end-initializations...
initializations to 'dotnetraw:'
soltake as SOL
end-initializations


mmetc
diskdata

Access data in text ﬁles in diskdata format (see Sections 3.4.3 and 10.2.3).

mmhttpurl

Access ﬁles that are stored on an HTTP enabled ﬁle serverfor reading, writing, ordeletion (via fdelete).
Example 1: the following command downloads and executes the Mosel BIM ﬁlemymodel.bim that is stored on the web server myserver:
mosel run mmhttp.url:http://myserver/mymodel.bim

Example 2: the following lines of Mosel code save data held in the model object
results to an XML format ﬁle on the server myserver that needs to be able toaccept HTTP PUT requests.
uses "mmxml"
declarations
results: xmldoc
end-declarations
save(results, "mmhttp.url:http://myserver/myresults.xml")


mmjavajava

Use a Java stream or a ByteBuffer in place of a ﬁle in Mosel (e.g. for redirecting
default Mosel streams to Java objects, see the example in Section 14.1.8).
Example 1: in a Java program, the line
mosel.setDefaultStream(XPRM.F_ERROR, "java:java.lang.System.out");(where mosel is an object of class XPRM) will redirect the Mosel error stream tothe default output stream of Java.
Example 2: the following lines will compile the Mosel model burglar2.mos tomemory and then load it from memory (full example in the ﬁleugcompmem.java).
XPRM mosel;
XPRMModel mod;ByteBuffer bimfile;// Buffer to store BIM filemosel = new XPRM();// Initialize Mosel// Prepare file names for compilation:
bimfile=ByteBuffer.allocateDirect(2048); // Create 2K byte buffer
mosel.bind("mybim", bimfile);// Associate Java obj. with a// Mosel name// Compile model to memory
mosel.compile("", "burglar2.mos", "java:mybim", "");
bimfile.limit(bimfile.position());
bimfile.rewind();// Mark end of data in buffer// Back to the beginning

mod=mosel.loadModel("java:mybim");mosel.unbind("mybim");bimfile=null;// Load BIM file from memory// Release memory

Fair Isaac Corporation Conﬁdential and Proprietary Information

171

Language extensions

jraw



Exchange of data between a Mosel model and the Java application running themodel; Java version of raw. See Section 14.1.7 for examples.

mmjobsshmem

Use shared memory instead of physical ﬁles for reading or writing data (e.g., forexchanging data between several models executed concurrently—one model
writing, several models reading—as shown in Section 17.2.3, or forcompiling/loading a model to/from memory from within another model, see
Section 17.2.2).

mempipeUse memory pipes for reading or writing data (e.g., for exchanging data between
several models executed concurrently—one model reading, several models writing;see Section ’Dantzig-Wolfe decomposition’ of thewhitepaper ’Multiple models and parallel solving with Mosel’ for an example).

rcmd

Starts the speciﬁed command in a new process and connects its standard input
and output streams to the calling Mosel instance.
Example: The following line starts a Mosel instance on a remote computer with
the name some_other_machine connecting to it via rsh. The command mosel-r starts Mosel in remote mode.
rcmd:rsh some_other_machine mosel -rrmt

Can be used with any routine expecting a physical ﬁle for accessing ﬁles on
remote instances. A particular instance can be speciﬁed by preﬁxing the ﬁle nameby its node number enclosed in square brackets. See the distributed computing
versions of the models in the whitepaper Multiple models and parallel solving with
Mosel for further examples.
Example: the following loads the BIM ﬁle mymodel.bim that is located at theparent node (-1) of the current instance into the model myModel of the Mosel
instance myInst.
load(myInst, myModel, "rmt:[-1]mymodel.bim")

The rmt driver can be combined with cb, sysfd, tmp or java, such as
fopen("rmt:tmp:log.txt", F_OUTPUT)

xsrv

Connects to the speciﬁed host running the Mosel Remote Launcher xprmsrv.
Optionally, the port number, a context name, a password, and environment variablesettings can be speciﬁed.
Example: the following starts a new Mosel instance (possibly using a different
Xpress version) on the current machine, redeﬁning Mosel’s current working
directory and the environment variable MYDATA.
xsrv:localhost/myxpress|MOSEL_CWD=C:\workdir|MYDATA=${MOSEL_CWD}\data

xssh



Secure version of the xsrv driver to connect to the speciﬁed host running theMosel Remote Launcher xprmsrv through a secure SSH tunnel.

mmoci
oci

Access an Oracle database for reading and writing in initializations blocks(see the whitepaper Using ODBC and other database interfaces with Mosel forfurther examples).
Example: the OCI driver is used with a connection string that contains the
database name, user name and password in the following format
initializations from "mmoci.oci:myusername/mypassword@dbname

Fair Isaac Corporation Conﬁdential and Proprietary Information

172

Language extensions



mmodbcodbc

Access data in external data sources via an ODBC connection (see Section 2.2.5
for an example).

mmsheetcsv

Access spreadsheets in CSV format. In addition to the standard options supported
by other Mosel spreadsheet drivers (such as grow for dynamic sizing of output
ranges and noindex for dense data format), this driver can be conﬁgured withﬁeld and decimal separators and also with the values representing ’true’ and ’false’.
Example: the following Mosel code for reading data from the ﬁle mydata.csv
sets the separator sign to ’;’, a comma is used as decimal separator, and theBoolean values true and false are represented by ’y’ and ’n’ respectively:
initializations from "mmsheet.csv:dsep=,;fsep=;;true=y;false=n;mydata.csv
A as "[B2:D8]"
B as "[E2:Z10](#3,#1)"! 3rd and 1st column from the range
end-initializations

Notice that with CVS format ﬁles there is no notion of range or ﬁeld names and thecell positions need to be used to specify the data location.



excel

Access data in MS Excel spreadsheets directly (see the example in Section
2.2.5.1).

xls

Access spreadsheets in Excel’s XLS format (see Section 2.2.5.3 for an example).

xlsx

Access spreadsheets in Excel’s XLSX and XLSM formats (usage in analogy to the
XLS example shown in Section 2.2.5.3).

mmsystempipeOpen a pipe and start an external program which is used as input or output stream
for a Mosel model.
Example: the following will start gnuplot and draw a 3-dimensional sphere withdata for the radius R and position (X,Y,Z) deﬁned in the Mosel model:
fopen("mmsystem.pipe:gnuplot -p", F_OUTPUT+F_LINBUF)
writeln('set parametric')
writeln('set urange [0:2⁎pi]')
writeln('set vrange [0:2⁎pi]')
writeln('set pm3d depthorder hidden3d 3')
writeln("splot cos(u) ⁎ cos(v) ⁎", R, "+", X,", sin(u) ⁎ cos(v)⁎", R, "+", Y,", sin(v)⁎", R, "+", Z, " notitle with pm3d")
writeln("pause mouse keypress,close")text

Use a (multiline) text as a ﬁle (see Section 17.6 for further detail on the type ’text’).
Example: the following will compile the model source held in the text
source_of_model to a BIM ﬁle in a temporary directory (ﬁleugcompfrommem.mos):public declarations
source_of_model=`SUBMODELSOURCE
model Burglar
uses 'mmxprs'
declarations
WTMAX = 102
ITEMS = 1..8
VALUE: array(ITEMS) of real
WEIGHT: array(ITEMS) of real
take: array(ITEMS) of mpvar
end-declarations

Fair Isaac Corporation Conﬁdential and Proprietary Information! Maximum weight allowed! Index range for items! Value of items! Weight of items! 1 if we take item i; 0 otherwise173

Language extensions

VALUE :: [15, 100, 90, 60, 40, 15, 10, 1]
WEIGHT:: [ 2, 20, 20, 30, 40, 30, 60, 10]! Objective: maximize total value
MaxVal:= sum(i in ITEMS) VALUE(i)⁎take(i)! Weight restriction
sum(i in ITEMS) WEIGHT(i)⁎take(i) <= WTMAX! All variables are 0/1
forall(i in ITEMS) take(i) is_binary
maximize(MaxVal)! Solve the problem
writeln("Solution:\n Objective: ", getobjval)
end-model
SUBMODELSOURCE`
end-declarations! Compile the model from memory
if compile("", "text:source_of_model", "tmp:burglar.bim")<>0 thenexit(1)
end-if

zlib
deflateHandles ﬁles compressed using the zlib compression format.
Example: decompress the ﬁle myfile.gz to myfile:
fcopy("zlib.deflate:myfile.gz", "myfile")

gzip

Handles ﬁles compressed using the gzip compression format.
Example: the following statement creates the compressed ﬁle myfile.gz frommyfile.txt:
fcopy("myfile.txt", "zlib.gzip:myfile.gz")

The reader is referred to the whitepaper Generalized ﬁle handling in Mosel that is provided as a part of
the Xpress documentation in the standard distribution and also on the Xpress website under ‘Product
Documentation’ for further explanation of this topic and a documented set of examples, including
some user-written I/O drivers.

17.2

Multiple models and parallel solving with mmjobs
The module mmjobs makes it possible to exchange information between models runningconcurrently—locally or in a network. Its functionality includes facilities for handling Mosel instances(e.g. connecting and disconnecting Mosel instances, access to remote ﬁles), model management (e.g.
compiling, running, or interrupting a model from within a second model), synchronization of concurrent
models based on event queues, and a shared memory I/O driver for an efﬁcient exchange of data
between models that are executed concurrently.
Several complete examples (including examples of Benders decomposition and Dantzig-Wolfe
decomposition) of the use of module mmjobs are described in the whitepaper Multiple models andparallel solving with Mosel that is provided as a part of the Xpress documentation and also on the’Product Documentation’ page of the Xpress website. We show here how to use the basic functionality
for executing a model from a second model.

17.2.1

Running a model from another model
As a test case, we shall once more work with model prime.mos from Section 8.3. In the ﬁrst instance,
we now show how to compile and run this model from a second model, runprime.mos:

Fair Isaac Corporation Conﬁdential and Proprietary Information

174

Language extensions
model "Run model prime"
uses "mmjobs"
declarations
modPrime: Model
event: Event
end-declarations! Compile 'prime.mos'
if compile("prime.mos")<>0 then exit(1); end-if
load(modPrime, "prime.bim")! Load bim filerun(modPrime, "LIMIT=50000")wait(2)! Start execution and! wait 2 seconds for an event

if isqueueempty then
! No event has been sent...
writeln("Model too slow: stopping it!")stop(modPrime)! ... stop the model,
wait! ... and wait for the termination event
end-if! An event is available: model finishedevent:=getnextevent
writeln("Exit status: ", getvalue(event))
writeln("Exit code : ", getexitcode(modPrime))
unload(modPrime)
end-model! Unload the submodel

The compile command generates the BIM ﬁle for the given submodel; the command load loads thebinary ﬁle into Mosel; and ﬁnally we start the model with the command run. The run command is not
used in its basic version (single argument with the model reference): here its second argument sets a
new value for the parameter LIMIT of the submodel.
In addition to the standard compile–load–run sequence, the model above shows some basic featuresof interaction with the submodel: if the submodel has not terminated after 2 seconds (that is, if it has
not sent a termination message) it is stopped by the master model. After termination of the submodel(either by ﬁnishing its calculations within less than 2 seconds or stopped by the master model) its
termination status and the exit value are retrieved (functions getvalue and getexitcode).
Unloading a submodel explicitly as shown here is only really necessary in larger applications that
continue after the termination of the submodel, so as to free the memory used by it.
Note: our example model shows an important property of submodels—they are running in parallel tothe master model and also to any other submodels that may have been started from the master model.
It is therefore essential to insert wait at appropriate places to coordinate the execution of the different
models.

17.2.2Compiling to memory
The model shown in the previous section compiles and runs a submodel. The default compilation of a
Mosel ﬁle filename.mos generates a binary model ﬁle filename.bim. To avoid the generation ofphysical BIM ﬁles for submodels we may compile the submodel to memory, making use of the conceptof I/O drivers introduced in Section 17.1.
Compiling a submodel to memory is done by replacing the standard compile and load commands by
the following lines (model runprime2.mos):
if compile("","prime.mos","shmem:bim")<>0 thenexit(1)
end-ifload(modPrime,"shmem:bim")
fdelete("shmem:bim")! Load bim file from memory...
! ... and release the memory block

Fair Isaac Corporation Conﬁdential and Proprietary Information

175

Language extensions

The full version of compile takes three arguments: the compilation ﬂags (e.g., use "g" for debugging),
the model ﬁle name, and the output ﬁle name (here a label preﬁxed by the name of the shared memory
driver). Having loaded the model we may free the memory used by the compiled model with a call tofdelete (this subroutine is provided by the module mmsystem).

17.2.3

Exchanging data between models
When working with submodels we are usually not just interested in executing the submodels, we also
wish to retrieve their results in the master model. This is done most efﬁciently by exchanging data in(shared) memory as shown in the model runprimeio.mos below. Besides the retrieval and printout of
the solution we have replaced the call to stop by sending the user event ‘STOPMOD’ to the submodel:
instead of simply terminating the submodel this event will make it interrupt its calculations and writeout the current solution. To make sure that the submodel is actually running at the point where we sentthe ‘STOPMOD’ event, we have also introduced an event sent from the submodel to the master toindicate the point of time when it starts the calculations (with heavy operating system loads the actual
submodel start may be delayed). Once the submodel has terminated (after sending the ‘STOPMOD’event we wait for the model’s termination message) we may read its solution from memory, using theinitializations block with the drivers raw (binary format) and shmem (read from shared memory).
model "Run model primeio"
uses "mmjobs"
declarations
modPrime: Model
NumP: integer
SetP: set of integer
STOPMOD = 2
MODREADY = 3
end-declarations! Number of prime numbers found! Set of prime numbers! "Stop submodel" user event! "Submodel ready" user event! Compile 'prime.mos'
if compile("primeio.mos")<>0 then exit(1); end-if
load(modPrime, "primeio.bim") ! Load bim file
! Disable model output
setdefstream(modPrime,"","null:","null:")run(modPrime, "LIMIT=35000") ! Start execution andwait! ... wait for an event
if getclass(getnextevent) <> MODREADY thenwriteln("Problem with submodel run")exit(1)
end-ifwait(2)! Let the submodel run for 2 seconds

if isqueueempty then
! No event has been sent...
writeln("Model too slow: stopping it!")send(modPrime, STOPMOD, 0)! ... stop the model, then wait
wait
end-ifdropnextevent! Discard end events
initializations from "bin:shmem:resdata"
NumP SetP as "SPrime"
end-initializations
writeln(SetP)! Output the result
writeln(" (", NumP, " prime numbers.)")unload(modPrime)
end-model

Fair Isaac Corporation Conﬁdential and Proprietary Information

176

Language extensions

We now have to modify the submodel (ﬁle primeio.mos) correspondingly: at its start it sends the‘MODREADY’ event to trigger the start of the time measurement in the master and it further needs tointercept the ‘STOPMOD’ event interrupting the calculations (via an additional test isqueueempty forthe repeat-until loop) and write out the solution to memory in the end:model "Prime IO"
uses "mmjobs"
parameters
LIMIT=100! Search for prime numbers in 2..LIMITOUTPUTFILE = "bin:shmem:resdata"! Location for output data
end-parameters
declarations
SNumbers: set of integer
SPrime: set of integer
MODREADY = 3
end-declarations! Set of numbers to be checked! Set of prime numbers! "Submodel ready" user event

send(MODREADY,0)! Send "model ready" event

SNumbers:={2..LIMIT}
writeln("Prime numbers between 2 and ", LIMIT, ":")
n:=2
repeatwhile (not(n in SNumbers)) n+=1
SPrime += {n}! n is a prime number
i:=nwhile (i<=LIMIT) do! Remove n and all its multiples
SNumbers-= {i}i+=n
end-dountil (SNumbers={} or not isqueueempty)
NumP:= getsize(SPrime)
initializations to OUTPUTFILE
NumP SPrime
end-initializations
end-model

Note: since the condition isqueueempty is tested only once per iteration of the repeat-until loop,
the termination of the submodel is not immediate for large values of LIMIT. If you wish to run thismodel with very large values, please see Section 15.2 for an improved implementation of the prime
number algorithm that considerably reduces its execution time.

17.2.4

Distributed computing
The module mmjobs not only allows the user to start several models in parallel on a given machine, italso makes it possible to execute models remotely and to coordinate their processing. With only few
additions, the model from Section 17.2.1 is extended to form model version runprimedistr.mos thatlaunches the submodel prime.mos on another Mosel instance (either on the local machine as in thepresent example or on some other machine within a network speciﬁed by its name or IP address in theconnect statement): we need to create a new Mosel instance (through a call to connect) and add an
additional argument to the load statement to specify the Mosel instance we wish to use. All else
remains the same as in the single-instance version.
model "Run model prime remotely"uses "mmjobs"

Fair Isaac Corporation Conﬁdential and Proprietary Information

177

Language extensions
declarations
moselInst: Mosel
modPrime: Model
event: Event
end-declarations! Compile 'prime.mos' locally
if compile("prime.mos")<>0 then exit(1); end-if! Start a remote Mosel instance:! "" means the node running this model
if connect(moselInst, "")<>0 then exit(2); end-if! Load bim file into remote instanceload(moselInst, modPrime, "rmt:prime.bim")run(modPrime, "LIMIT=50000")wait(2)! Start execution and! wait 2 seconds for an event

if isqueueempty then
! No event has been sent...
writeln("Model too slow: stopping it!")stop(modPrime)! ... stop the model, then wait
wait
end-if! An event is available: model finishedevent:=getnextevent
writeln("Exit status: ", getvalue(event))
writeln("Exit code : ", getexitcode(modPrime))
unload(modPrime)
end-model! Unload the submodelThis model can be extended to include data exchange between the master and the submodel exactly inthe same way as in the example of Section 17.2.3. The main difference (besides the connection to aremote instance) lies in the use of the driver rmt to denote the Mosel instance where data is to be
saved. In our case, we wish to save data on the instance running the master model, meaning that weneed to use the rmt: preﬁx when writing output within the submodel. The new output ﬁle name ispassed into the submodel via the runtime parameter OUTPUTFILE:
run(modPrime, "LIMIT=35000,OUTPUTFILE=bin:rmt:shmem:resdata")

The master model then simply reads as before from its own instance:
initializations from "bin:shmem:resdata"
NumP SetP as "SPrime"
end-initializations

The Mosel model for the extended version including data exchange is provided in the ﬁlerunprimeiodistr.mos.

17.3

Graphics and GUIs
Different components of FICO Xpress Optimization provide graphics and GUI functionality for Mosel
models:Users may enrich their Mosel models with graphical output using the module mmsvg.
Xpress Insight embeds Mosel models into a multi-user application for deploying optimization
models in a distributed client-server architecture. Through the Xpress Insight GUI, business users
interact with Mosel models to evaluate different scenarios and model conﬁgurations without
directly accessing to the model itself.

Fair Isaac Corporation Conﬁdential and Proprietary Information

178

Language extensions

XML is a widely used data format, particularly in the context of web-based applications. The
Mosel module mmxml provides functionality for generating and handling XML documents.
mmxml can also be used to produce HTML format output from Mosel that can be incorporated
into Xpress Insight applications.

The functionality of modules mmxml and mmsvg is documented in the Mosel Language Reference
Manual. Xpress Insight has several manuals and guides for developers and GUI users, most importantly
the ’Xpress Insight Developer Guide’ and ’Xpress Insight Web Client User Guide’; the corresponding
examples are located in the subdirectory examples/insight of the Xpress distribution.

17.3.1

Drawing user graphs with mmsvg
The graphic in Figure 17.1 is an example of using mmsvg to produce a graphical representation of thesolution to the transport problem from Section 3.2.
It was obtained by calling the following procedure draw_solution at the end of the model ﬁle (that is,after the call to minimize).
procedure draw_solution
declarations
YP: array(PLANT) of integer
YR: array(REGION) of integer
end-declarations! y-coordinates of plants! y-coordinates of sales regions! Scale the size of the displayed graphsvgsetgraphviewbox(0,0,4,getsize(REGION)+1)
svgsetgraphscale(100)! Determine y-coordinates for plants and regions
ct:= 1+floor((getsize(REGION)-getsize(PLANT))/2)
forall(p in PLANT, ct as counter) YP(p):= ct
ct:=1
forall(r in REGION, ct as counter) YR(r):= ct! Draw the plants
svgaddgroup("PGr", "Plants", svgcolor(0,63,95))
forall(p in PLANT) svgaddtext(0.55, YP(p)-0.1, p)! Draw the sales regions
svgaddgroup("RGr", "Regions", svgcolor(0,157,169))
forall(r in REGION) svgaddtext(3.1, YR(r)-0.1, r)! Draw all transport routes
svgaddgroup("TGr", "Routes", SVG_GREY)
forall(p in PLANT, r in REGION | exists(TRANSCAP(p,r)) )svgaddline(1, YP(p), 3, YR(r))! Draw the routes used by the solution
svgaddgroup("SGr", "Solution", SVG_ORANGE)
forall(p in PLANT, r in REGION | exists(flow(p,r)) and getsol(flow(p,r)) > 0)
svgaddarrow(1, YP(p), 3, YR(r))! Save graphic in SVG format
svgsave("transport.svg")! Display the graphic
svgrefreshsvgwaitclose
end-procedure

Fair Isaac Corporation Conﬁdential and Proprietary Information

179

Language extensions

Figure 17.1: User graph for the transport problem

17.3.2XML and HTMLHTML ﬁles are simple text ﬁles—their contents can be generated as free-format output from Mosel (seefor example Section 10.2). However, more elegantly we can use Mosel’s XML module mmxml togenerate HTML documents.
17.3.2.1

mmxml

The module mmxml provides an XML parser and generator for the manipulation of XML documentsfrom Mosel models. An XML document is stored as a list of nodes. mmxml supports the node types’element’, ’text’, ’comment’, CDATA, ’processing instruction’ and DATA (see section mmxml of the Mosel
Language Reference Manual for further detail). Each node is characterized by a name and a value.
Element nodes have also an ordered list of child nodes. The root node is a special element node withno name, no parent and no successor that includes the entire document as its children.
The type xmldoc represents an XML document stored in the form of a tree. Each node of the tree is
identiﬁed by a node number (an integer) that is attached to the document (i.e. a node number cannot beshared by different documents and in two different documents the same number represents two
different nodes). The root node of the document has number 0. Nodes can be retrieved using a pathsimilar to a directory path used to locate a ﬁle (usually called XML path).
17.3.2.2

Reading and writing XML data

Data for the ’Transport’ problem has so far been given as a text data ﬁle in initializations format.
We now wish to read in the same data from the XML ﬁle transprt.xml shown here:<transport fuelcost="17"><demand><region name="Scotland">2840</region><region name="North">2800</region>

Fair Isaac Corporation Conﬁdential and Proprietary Information

180

Language extensions<region name="SWest">2600</region><region name="SEast">2820</region><region name="Midlands">2750</region></demand><plantdata><plant name="Corby"><capacity>3000</capacity><cost>1700</cost></plant><plant name="Deeside"><capacity>2700</capacity><cost>1600</cost></plant><plant name="Glasgow"><capacity>4500</capacity><cost>2000</cost></plant><plant name="Oxford"><capacity>4000</capacity><cost>2100</cost></plant></plantdata><routes><route from="Corby" to="North" capacity="1000" distance="400"/><route from="Corby" to="SWest" capacity="1000" distance="400"/><route from="Corby" to="SEast" capacity="1000" distance="300"/><route from="Corby" to="Midlands" capacity="2000" distance="100"/><route from="Deeside" to="Scotland" capacity="1000" distance="500"/><route from="Deeside" to="North" capacity="2000" distance="200"/><route from="Deeside" to="SWest" capacity="1000" distance="200"/><route from="Deeside" to="SEast" capacity="1000" distance="200"/><route from="Deeside" to="Midlands" capacity="300" distance="400"/><route from="Glasgow" to="Scotland" capacity="3000" distance="200"/><route from="Glasgow" to="North" capacity="2000" distance="400"/><route from="Glasgow" to="SWest" capacity="1000" distance="500"/><route from="Glasgow" to="SEast" capacity="200" distance="900"/><route from="Oxford" to="North" capacity="2000" distance="600"/><route from="Oxford" to="SWest" capacity="2000" distance="300"/><route from="Oxford" to="SEast" capacity="2000" distance="200"/><route from="Oxford" to="Midlands" capacity="500" distance="400"/></routes></transport>

The Mosel code (ﬁle transport_xml.mos) for reading this XML ﬁle ﬁrst loads the entire document(load). We then need to retrieve the nodes we are interested in from the XML document tree structure.
This is achieved by selecting lists of nodes that satisfy some speciﬁed condition (here: a speciﬁc XML
path that describes the location of the desired nodes in the document, such astransport/demand/region). XML documents are saved as text, we therefore use functions likegetrealvalue or getstrattr to retrieve data and indices of the desired type into the model data
structures.
uses "mmxml"
declarations
REGION: set of string
PLANT: set of string! Set of customer regions! Set of plants

DEMAND: array(REGION) of real
! Demand at regions
PLANTCAP: array(PLANT) of real
! Production capacity at plants
PLANTCOST: array(PLANT) of real
! Unit production cost at plants
TRANSCAP: dynamic array(PLANT,REGION) of real
! Capacity on each route plant->region
DISTANCE: dynamic array(PLANT,REGION) of real
! Distance of each route plant->region
FUELCOST: real
! Fuel cost per unit distance
AllData: xmldoc

Fair Isaac Corporation Conﬁdential and Proprietary Information! XML document181

Language extensions
NodeList: list of integer
end-declarations! List of XML nodesload(AllData, "transprt.xml")! Load the entire XML document

getnodes(AllData, "transport/demand/region", NodeList)
forall(l in NodeList)! Read demand data
DEMAND(getstrattr(AllData,l,"name")):= getrealvalue(AllData, l)getnodes(AllData, "transport/plantdata/plant", NodeList)
forall(l in NodeList) do! Read plant data
PLANTCAP(getstrattr(AllData,l,"name")):=
getrealvalue(AllData, getnode(AllData,l,"capacity"))
PLANTCOST(getstrattr(AllData,l,"name")):=
getrealvalue(AllData, getnode(AllData,l,"cost"))end-do! Read routes data
getnodes(AllData, "transport/routes/route", NodeList)
forall(l in NodeList) do
DISTANCE(getstrattr(AllData,l,"from"),getstrattr(AllData,l,"to")):=
getrealattr(AllData,l,"distance")
TRANSCAP(getstrattr(AllData,l,"from"),getstrattr(AllData,l,"to")):=
getrealattr(AllData,l,"capacity")
end-do! Read 'fuelcost' attribute
FUELCOST:= getrealattr(AllData, getnode(AllData, "transport"), "fuelcost")

In the model extract above we have used several simple XML path speciﬁcations to retrieve lists ofnodes from the XML document. Such queries can take more complicated forms, including tests on
node values (all plants with capacity >3500) or attributes (all routes to ’Scotland’)—see the chapter on
mmxml in the Mosel Language Reference Manual for further detail.
getnodes(AllData, "transport/plantdata/plant/capacity[number()>3500]/..", NodeList)
getnodes(AllData, "transport/routes/route[@to='Scotland']", NodeList)

We now also want to output the optimization results in XML format. As a ﬁrst step, we create a root
element ’solution’ in the XML document ResData. In a well-formed XML document, all elements need
to form a tree under the root element. All following nodes (containing the solution information perplant) are therefore created as element nodes under the ’solution’ node. The objective function solution
value and the execution date of the model are saved as attributes to the ’solution’ tag. And ﬁnally, we
use save to write an XML ﬁle or display a node with the (sub)tree formed by its children.
declarations
ResData: xmldoc
Sol,Plant,Reg,Total: integer
end-declarations! XML document! XML nodes

Sol:=addnode(ResData, 0, XML_ELT, "solution")! Create root node "solution"
setattr(ResData, Sol, "Objective", MinCost.sol)! Obj. value as attributesetattr(ResData, Sol, "RunDate", text(datetime(SYS_NOW)))
forall(p in PLANT) do
Plant:=addnode(ResData, Sol, XML_ELT, "plant") ! Add a node to "solution"
setattr(ResData, Plant, "name", p)! ... with attribute "name"forall(r in REGION | flow(p,r).sol>0) do
Reg:=addnode(ResData, Plant, XML_ELT, "region") ! Add a node to "plant"
setattr(ResData, Reg, "name", r)! ... with attribute "name"setvalue(ResData, Reg, flow(p,r).sol)! ... and solution value
end-doTotal:=addnode(ResData, Plant, "total",sum(r in REGION)flow(p,r).sol)! Add node with total flow
end-dosave(ResData, "transportres.xml")
save(ResData, Sol, "")Fair Isaac Corporation Conﬁdential and Proprietary Information! Save solution to XML format file
! Display XML format solution on screen

182Language extensions

The Mosel code printed above will create a ﬁle transportres.xml with the following contents:<?xml version="1.0" encoding="iso-8859-1"?><solution Objective="8.1018e+07" RunDate="2012-10-17T14:00:50,664"><plant name="Corby"><region name="North">80</region><region name="SEast">920</region><region name="Midlands">2000</region><total>3000</total></plant><plant name="Deeside"><region name="North">1450</region><region name="SWest">1000</region><region name="Midlands">250</region><total>2700</total></plant><plant name="Glasgow"><region name="Scotland">2840</region><region name="North">1270</region><total>4110</total></plant><plant name="Oxford"><region name="SWest">1600</region><region name="SEast">1900</region><region name="Midlands">500</region><total>4000</total></plant></solution>

17.3.2.3

Generating HTMLAn HTML ﬁle is generated and written out just like XML documents. The name of the root element inthis case usually is ’html’. Below follows an extract of the code (example ﬁle transport_html.mos)that generates the HTML page shown in Figure 17.2. Notice the use of copynode to insert the samenode/subtree at different positions in the XML document. By default, new nodes created with addnode
are appended to the end of the node list of the speciﬁed parent node. This corresponds to using thevalue XML_LASTCHILD for the (optional) positioning argument of subroutines creating new nodes.
declarations
ResultHTML: xmldoc
Root, Head, Body, Style, Title, Table, Row, Cell, EmptyCell: integer
end-declarations
Root:= addnode(ResultHTML, 0, XML_ELT, "html")! Root node
Head:= addnode(ResultHTML, Root, XML_ELT, "head")! Element node
Style:= addnode(ResultHTML, Head, XML_ELT, "style","body {font-family: Verdana, Geneva, Helvetica, Arial, sans-serif;" +" color: 003f5f; background-color: d8e3e9 }\n" +"table td {background-color: e9e3db; color: 003f5f; text-align: right }\n" +"table th {background-color: f7c526; color: 003f5f}")
setattr(ResultHTML, Style, "type", "text/css")! Set an attributeBody:= addnode(ResultHTML, Root, XML_ELT, "body")! Body of HTML page
Title:= addnode(ResultHTML, Body XML_ELT, "h2", "Transportation Plan")
Table:= addnode(ResultHTML, Body, XML_ELT, "table") ! 'table' element
setattr(ResultHTML, Table, "width", '100%')! Set some attributes
setattr(ResultHTML, Table, "border", 0)
Row:= addnode(ResultHTML, Table, XML_ELT, "tr")! Table row element
EmptyCell:= addnode(ResultHTML, Row, XML_ELT, "td") ! Table cell element
setattr(ResultHTML, EmptyCell, "width", '5%')! Set an attribute
Cell:= addnode(ResultHTML, Row, "td", "Total cost: " +textfmt(MinCost.sol,6,2)) ! Table cell element with contents
Cell:= addnode(ResultHTML, Cell, XML_DATA, "&#163;") ! DATA node
Cell:= addnode(ResultHTML, Row, "td", text(datetime(SYS_NOW)))

Fair Isaac Corporation Conﬁdential and Proprietary Information

183

Language extensions
EmptyCell:=! Node created by copying a node
copynode(ResultHTML, EmptyCell, ResultHTML, Row, XML_LASTCHILD)...
save(ResultHTML, "transportres.html")
save(ResultHTML, Table, "")! Write the HTML file
! Display table def. on screen

The resulting HTML page might now look as shown in Figure 17.2.

Figure 17.2: HTML page generated by Mosel

17.3.3

Xpress InsightFor embedding a Mosel model into Xpress Insight, we make a few edits to the model. All functionalitythat is needed to establish the connection between Mosel and Xpress Insight is provided by mminsightthat now needs to be loaded. Since Xpress Insight manages the data scenarios, we only need to read indata from the original sources when loading the so-called baseline scenario into Xpress Insight(triggered by the test of insightgetmode=INSIGHT_MODE_LOAD in the model below), for modelruns started from Xpress Insight (that is, in the case of insightgetmode=INSIGHT_MODE_RUN) the
scenario data will be input directly from Xpress Insight at the insertion point marked withinsightpopulate. For standalone execution (insightgetmode=INSIGHT_MODE_NONE) the model
defaults to its original behavior, that is, reading the data from ﬁle followed by deﬁnition and solving of
the optimization problem. Furthermore, the solver call to start the optimization is replaced byinsightminimize / insightmaximize. Please also note that any model entities to be managed by
Xpress Insight need to be declared as public—in the following code example this marker has been
applied to the entire declarations block, alternatively it can be added to individual entity
declarations.
model "Transport (Xpress Insight)"
uses "mmxprs", "mminsight"
public declarations
REGION: set of string
PLANT: set of string! Set of customer regions! Set of plants

DEMAND: array(REGION) of real
! Demand at regions
PLANTCAP: array(PLANT) of real
! Production capacity at plants
PLANTCOST: array(PLANT) of real
! Unit production cost at plants
TRANSCAP: dynamic array(PLANT,REGION) of real
! Capacity on each route plant->region
DISTANCE: dynamic array(PLANT,REGION) of real

Fair Isaac Corporation Conﬁdential and Proprietary Information

184

Language extensions

FUELCOST: real

! Distance of each route plant->region! Fuel cost per unit distance

MaxCap: array(PLANT) of linctr! Capacity constraints
flow: dynamic array(PLANT,REGION) of mpvar! Flow on each routeend-declarations
procedure readdata
initializations from 'transprt.dat'
DEMAND[PLANTCAP,PLANTCOST] as 'PLANTDATA'[DISTANCE,TRANSCAP] as 'ROUTES'FUELCOST
end-initializations
end-procedurecase insightgetmode of
INSIGHT_MODE_LOAD: doreaddataexit(0)
end-doINSIGHT_MODE_RUN: insightpopulate
INSIGHT_MODE_NONE: readdata
else
writeln("Unknown run mode")exit(1)
end-case! Data for baseline! Input baseline data and! stop the model run here
! Inject scenario data and continue! Input baseline data and continue! Create the flow variables that exist
forall(p in PLANT, r in REGION | exists(TRANSCAP(p,r)) ) create(flow(p,r))! Objective: minimize total cost
MinCost:= sum(p in PLANT, r in REGION | exists(flow(p,r)))(FUELCOST ⁎ DISTANCE(p,r) + PLANTCOST(p)) ⁎ flow(p,r)! Limits on plant capacity
forall(p in PLANT) MaxCap(p):= sum(r in REGION) flow(p,r) <= PLANTCAP(p)! Satisfy all demands
forall(r in REGION) sum(p in PLANT) flow(p,r) = DEMAND(r)! Bounds on flows
forall(p in PLANT, r in REGION | exists(flow(p,r)))flow(p,r) <= TRANSCAP(p,r)
insightminimize(MinCost)! Solve the problem through Xpress Insight

The handling of model entities by Xpress Insight can be conﬁgured via annotations (see Chapter 18 fordetail), for example to deﬁne aliases to be displayed in the UI in place of the model entity names, or to
select which data entities are to be treated as inputs or results respectively. The annotations deﬁned by
Xpress Insight form the category insight, the following model extract shows some example
deﬁnitions for the ’Transport’ problem, please refer to the Xpress Insight Mosel Interface Manual for acomplete documentation.
!@insight.manage=input
public declarations!@insight.alias Customer regions
REGION: set of string! Set of customer regions!@insight.alias Plants
PLANT: set of string! Set of plants!@insight.alias Demand
DEMAND: array(REGION) of real
! Demand at regions!@insight.alias Production capacity
PLANTCAP: array(PLANT) of real
! Production capacity at plants!@insight.alias Unit production cost
PLANTCOST: array(PLANT) of real
! Unit production cost at plants!@insight.alias Capacity on each route

Fair Isaac Corporation Conﬁdential and Proprietary Information

185

Language extensions
TRANSCAP: dynamic array(PLANT,REGION) of real!@insight.alias Distance per route
DISTANCE: dynamic array(PLANT,REGION) of real!@insight.alias Fuel cost per unit distance
FUELCOST: real
! Fuel cost per unit distanceend-declarations!@insight.manage=result
public declarations!@insight.alias Production capacity limits
MaxCap: array(PLANT) of linctr! Capacity constraints!@insight.alias Amount shipped
flow: dynamic array(PLANT,REGION) of mpvar! Flow on each route!@insight.hidden=true
MincostSol: real!@insight.alias Total
pltotal: array(PLANT) of real
! Solution: production per plant
end-declarations

Xpress Insight expects models to be provided in compiled form, that is, as BIM ﬁles. An Xpress Insight
app archive is a ZIP archive that contains the BIM ﬁle and the optional subdirectories
model_resources (data ﬁles), client_resources (custom view deﬁnitions), and source (Mosel
tomodel source ﬁles). When developing an Insight app with Xpress Workbench, select the button
create the app archive or

to publish the app directly to Insight.

Figure 17.3: Xpress Insight web view showing a VDL view for the transportation problem

If we wish to deploy an optimization app to the Xpress Insight Web Client we need to take into accountthat besides the ’Entity browser’ view that lists all managed model entities there are no default views:
any visualization of input or result data needs to be explicitly implemented as views. The screenshot in
Figure 17.3 shows a VDL (View Deﬁnition Language) view for the transportation example with editable
input data and a ’Run’ button that triggers re-solving of the optimization problem with the scenario data
displayed on screen. Such views can be created via a drag-and-drop editor in Xpress Workbench (thescreenshot in Figure 17.4 shows the design view of the VDL ﬁle that deﬁnes the webview in the previous
ﬁgure). The complete set of ﬁles is provided in the app archive transport_insight.zip. Pleaserefer to the Xpress Insight Developer Manual for further detail on view deﬁnition using the VDL markup
language or the Xpress Insight Javascript API.

Fair Isaac Corporation Conﬁdential and Proprietary Information

186

Language extensions

Figure 17.4: VDL view designer in Xpress Workbench

17.4

Solvers
In this user guide we have explained the basics of working with Mosel, focussing on the formulation ofLinear and Mixed Integer Programming problems and related solution techniques. However, the Mosel
language is not limited to certain types of Mathematical Programming problems.
The module mmnl extends the Mosel language with functionality for handling general non-linear
constraints. This module (documented in the Mosel Language Reference Manual) does not contain any
solver on its own. In combination with mmxprs you can use it to formulate and solve QCQP
(Quadratically Constrained Quadratic Programming) problems through the QCQP solvers within Xpress
Optimizer.
All other solvers of FICO Xpress Optimization (Xpress NonLinear for solving non-linear problems, andXpress Kalis for Constraint Programming, CP) are provided with separate manuals and their own setsof examples. Please see the Xpress website for an overview of the available products.
With Mosel it is possible to combine several solvers to formulate hybrid solution approaches forsolving difﬁcult application problems. The whitepaper Hybrid MIP/CP solving with Xpress Optimizer andXpress Kalis, available for download from the Xpress website, gives several examples of hybrid solving
with LP/MIP and Constraint Programming.
Below follow a few examples for some of the solvers mentioned above.

17.4.1QCQP solving with Xpress Optimizer
In Section 12.1 we have solved a quadratically constrained problem by a recursion algorithm. One might
be tempted to try solving this problem with a QCQP solver. Unfortunately, it does not meet theproperties required by the deﬁnition of QCQP problems: problems solvable by QCQP must not containequality constraints with quadratic terms (reformulation as two inequalities will not work either) sincesuch constraints do not satisfy the convexity condition. We shall see below how to solve this problem
with the general non-linear solver Xpress NonLinear (using SLP).
Let us therefore take a look at a different problem: the task is to distribute a set of points represented
by tuples of x-/y-coordinates on a plane minimizing the total squared distance between all pairs of
points. For each point i we are given a target location (CXi , CYi ) and the (square of the) maximum
allowable distance to this location, the (squared) radius Ri around this location.

Fair Isaac Corporation Conﬁdential and Proprietary Information

187

Language extensions

In mathematical terms, we have two decision variables xi and yi for the coordinates of every point i. The
objective to minimize the total squared distance between all points is expressed by the following sum.
N–1 X
N 
X

xi – xj

2+ yi – yj2 

i=1 j=i+1

For every point i we have the following quadratic inequality.
(xi – CXi )2 + (yi – CYi )2 ≤ Ri
The resulting Mosel model (ﬁle airport_qp.mos) looks thus.
model "airport"
uses "mmxprs", "mmnl"
declarations
RN: range
R: array(RN) of real
CX,CY: array(RN) of real
x,y: array(RN) of mpvar
LimDist: array(RN) of nlctr
end-declarations! Set of airports! Square of max. distance to given location! Target location for each point! x-/y- coordinates

initialisations from "airport.dat"
CY CX R
end-initialisations! Set bounds on variables
forall(i in RN) do-10<=x(i); x(i)<=10-10<=y(i); y(i)<=10
end-do! Objective: minimise the total squared distance between all points
TotDist:= sum(i,j in RN | i<j) ((x(i)-x(j))^2+(y(i)-y(j))^2)! Constraints: all points within given distance of their target location
forall(i in RN)
LimDist(i):= (x(i)-CX(i))^2+(y(i)-CY(i))^2 <= R(i)setparam("XPRS_verbose", true);
minimise(TotDist);
writeln("Solution: ", getobjval);
forall(i in RN) writeln(i, ": ", getsol(x(i)), ", ", getsol(y(i)))
end-model

A QCQP matrix can be exported to a text ﬁle (option "" for MPS or "l" LP format) through thewriteprob function of Xpress Optimizer. That is, you need to add the following lines to your model
after the problem deﬁnition:
setparam("XPRS_loadnames", true)loadprob(TotDist)
writeprob("airport.mat","l")A graphical representation of the result with mmsvg, obtained with the following lines of Mosel code, isshown in Figure 17.5.
! Set the size of the displayed graphsvgsetgraphviewbox(-10,-10,10,10)

Fair Isaac Corporation Conﬁdential and Proprietary Information

188

Language extensions
svgsetgraphscale(20)! Draw the target locations
svgaddgroup("T", "Target area", SVG_SILVER)
svgsetstyle(SVG_FILL,SVG_CURRENT)
forall(i in RN) svgaddcircle(CX(i), CY(i), sqrt(R(i)))! Draw the solution points
svgaddgroup("S", "Solution", SVG_BLUE)
forall(i in RN) svgaddpoint(x(i).sol, y(i).sol)! Output to filesvgsave("airport.svg")! Update the display
svgrefreshsvgwaitclose

Figure 17.5: Result graphic in SVG format

17.4.2

Xpress NonLinear
The following example solves the non-linear ﬁnancial planning problem from Section 12.1 with Xpress
NonLinear (using the SLP solver). The deﬁnition of constraints is straightforward (nonlinear constraints
have the type nlctr with mmxnlp). Other functionality contributed by the module mmxnlp that appears
in this model are the setinitval subroutine—used for setting start values for the decision variables—
and the overloaded minimize subroutine for loading and solving nonlinear problems. Here, we simply
need to become feasible and therefore use 0 as argument to minimize. Xpress NonLinear
automatically selects a solver among the installed solvers of the Xpress suite (Simplex, Barrier, SLP, orKnitro) depending on the detected problem type. We know that this problem can be solved by recursion
and therefore preselect the SLP solver by setting the parameter XNLP_SOLVER.
model "Recursion (NLP)"
uses "mmxnlp"! Use Xpress NonLinear

declarations
NT=6
QUARTERS=1..NT
M,P,V: array(QUARTERS) of real

! Time horizon! Range of time periods! Paymentsinterest: array(QUARTERS) of mpvar ! Interest
net: array(QUARTERS) of mpvar! Net

Fair Isaac Corporation Conﬁdential and Proprietary Information

189

Language extensionsbalance: array(QUARTERS) of mpvar
rate: mpvar
end-declarations! Balance! Interest rate

M:: [-1000, 0, 0, 0, 0, 0]
P:: [206.6, 206.6, 206.6, 206.6, 206.6, 0]
V:: [-2.95, 0, 0, 0, 0, 0]
setinitval(rate, 0)! Set initial values for variables
forall(t in QUARTERS) setinitval(balance(t), 1)! net = payments - interest
forall(t in QUARTERS) net(t) = (M(t)+P(t)+V(t)) - interest(t)! Money balance across periods
forall(t in QUARTERS) balance(t) = if(t>1, balance(t-1), 0) - net(t)! Interest rate
forall(t in 2..NT) -(365/92)⁎interest(t) + balance(t-1) ⁎ rate = 0
interest(1) = 0! Initial interest is zero
forall(t in QUARTERS) net(t) is_free
forall(t in 1..NT-1) balance(t) is_freebalance(NT) = 0! Final balance is zero! setparam("XNLP_VERBOSE",true)
setparam("XNLP_SOLVER", 0)! Uncomment to see detailed output! Use the SLP solver

minimize(0)! Solve the problem (get feasible)! Print the solution
writeln("\nThe interest rate is ", getsol(rate))
write(strfmt("t",5), strfmt(" ",4))
forall(t in QUARTERS) write(strfmt(t,5), strfmt(" ",3))write("\nBalances ")forall(t in QUARTERS) write(strfmt(getsol(balance(t)),8,2))
write("\nInterest ")forall(t in QUARTERS) write(strfmt(getsol(interest(t)),8,2))
writelnend-model

The results displayed by this model are exactly the same as those we have obtained from the recursion
algorithm in Section 12.1.

17.4.3

Xpress Kalis
Constraint Programming is an entirely different method of representing and solving problems
compared to the Mathematical Programming approaches we have employed so far in this manual.
Consequently, the Mosel module kalis deﬁnes its own set of types that behave differently from their
Mathematicl Programming counterparts, including cpvar, cpfloatvar and cpctr for decisionvariables and constraints, but also cpbranching for search strategies (a standard constituent of CP
models) and aggregate modeling objects such as cptask and cpresource.
Below we show the CP implementation of a binpacking example from the book ‘Applications of
optimization with Xpress-MP’ (Section 9.4 ‘Backing up ﬁles’). The problem is to save sixteen ﬁles ofdifferent sizes onto empty disks of the same ﬁxed capacity minimizing the total number of disks thatare used.
Our model formulation remains close to the Mixed Integer Programming formulation and yet showssome speciﬁties of Constraint Programming. Two sets of decision variables are used, savef indicating
the choice of disk for ﬁle f and usefd the amount of diskspace used by a ﬁle on a particular disk.
Whereas the variables savef simply take integer values in a speciﬁed interval, each of the usefd
variables may take only two values, 0 or SIZEf . For every ﬁle / disk combination we establish the logical

Fair Isaac Corporation Conﬁdential and Proprietary Information

190

Language extensions

relation
savef = d ⇔ usefd = SIZEf
a constraint that cannot be stated in this form in a Mathematical Programming model. A second,so-called global constraint relation is used to state the maximum relation for calculating the number ofdisks used:diskuse = maximum(savef∈DISKS )And ﬁnally, the constraint limiting the capacity available per disk is a linear inequality that could occur inthe same form in a Mathematical Programming model.
X∀d ∈ DISKS :usefd ≤ CAP
f∈FILESThis is the complete Mosel CP model for the binpacking problem.
model "D-4 Bin packing (CP)"uses "kalis"declarations
ND: integer
FILES = 1..16
DISKS: range! Number of floppy disks! Set of files! Set of disks

CAP: integer
SIZE: array(FILES) of integer
end-declarations! Floppy disk size
! Size of files to be saved

initializations from 'd4backup.dat'
CAP SIZE
end-initializations! Provide a sufficiently large number of disks
ND:= ceil((sum(f in FILES) SIZE(f))/CAP)DISKS:= 1..ND
finalize(DISKS)
setparam("kalis_default_lb", 0)declarations
save: array(FILES) of cpvar
use: array(FILES,DISKS) of cpvar
diskuse: cpvar
end-declarations! Disk a file is saved on! Space used by file on disk
! Number of disks used

! Set variable domains
forall(f in FILES) setdomain(save(f), DISKS)
forall(f in FILES, d in DISKS) setdomain(use(f,d), {0, SIZE(f)})! Correspondence between disk choice and space used
forall(f in FILES, d in DISKS) equiv(save(f)=d, use(f,d)=SIZE(f))! Limit the number of disks useddiskuse = maximum(save)! Capacity limit of disks
forall(d in DISKS) sum(f in FILES) use(f,d) <= CAP! Minimize the total number of disks used
if not cp_minimize(diskuse) thenwriteln("Problem infeasible")
end-ifFair Isaac Corporation Conﬁdential and Proprietary Information

191

Language extensions! Solution printing
writeln("Number of disks used: ", getsol(diskuse))
forall(d in 1..getsol(diskuse)) dowrite(d, ":")
forall(f in FILES) write( if(getsol(save(f))=d , " "+SIZE(f), ""))
writeln(" space used: ", getsol(sum(f in FILES) use(f,d)))
end-doend-modelThis implementation is one of several possible formulations of this problem as a CP model. An
alternative and generally more efﬁcient model being the formulation as a cumulative schedulingproblem, where the disks are represented by a single resource of discrete capacity, the ﬁles to savecorrespond to tasks of duration 1 with a resource requirement deﬁned by the ﬁle size. The objective inthis case is to minimize the duration of the schedule (= number of disks used). The interested reader isrefered to the Xpress Kalis User Guide for a detailed discussion of this problem.

17.5

Date and time data types
The module mmsystem of the standard distribution of Mosel deﬁnes the types date (calendar day:day, month, and year), time (time of the day in milliseconds), and datetime (combination of the ﬁrsttwo) for working with date and time related data in Mosel models. We show here some examples of

17.5.1reading and writing dates and times from/to ﬁle,

formatting dates and times,using sets of constant dates and times for indexing arrays,transformation from/to the underlying numerical representation,



applying operations (comparison, addition, difference, sorting),

enumerating dates and times.

Initializing dates and times
The following line prints out the current date and time (using the default format):writeln("Today: ", date(SYS_NOW), ", current local time: ", time(SYS_NOW),"UTC time: ", gettime(datetime(timestamp)) )When we wish to read data from a ﬁle, the formatting of dates and times needs to be adapted to theformat used in the ﬁle. For example, consider the following data ﬁle (datetime.dat)
Time1:
Time2:
Time3:"4pm""16h00""16:00:00"

Date1:
Date2:
Date3:"2-20-2002""20/02/02""20-Feb-2002"

A Mosel model reading in this data ﬁle may look thus (ﬁle dates.mos).
declarations
t: timed: dateFair Isaac Corporation Conﬁdential and Proprietary Information

192

Language extensions
end-declarations
setparam("timefmt", "%h%p")
setparam("datefmt", "%m-%d-%y")! h: hours in 1-12, p: am/pm! m: month, d: day, y: year

initializations from "datetime.dat"t as "Time1"
d as "Date1"
end-initializations
writeln(d, ", ", t)setparam("timefmt", "%Hh%0M")
setparam("datefmt", "%0d/%0m/%0Y")! H: hours in 0-23, M: minutes! Y: year in 0-99
! 0: fill spaces with '0'

initializations from "datetime.dat"t as "Time2"
d as "Date2"
end-initializations
writeln(d, ", ", t)setparam("timefmt", "%H:%0M:%0S")
setparam("datefmt", "%d-%N-%y")! S: seconds! N: use month names

initializations from "datetime.dat"t as "Time3"
d as "Date3"
end-initializations
writeln(d, ", ", t)

For the encoding of date and time format strings please refer to the documentation of the parameters
datefmt and timefmt in the ’Mosel Language Reference Manual’.
Date3 in this example uses a month name, not a number. The default 3-letter abbreviations of month
names can be changed (e.g., translated) by redeﬁning the parameter monthnames. For instance, a datewritten in French, such as
Date4:"20 fevrier 2002"

is read by the following Mosel code:
setparam("datefmt", "%d %N %y")setparam("monthnames", "janvier fevrier mars avril mai juin juillet " +"aout septembre octobre novembre decembre")initializations from "datetime.dat"
d as "Date4"
end-initializations
writeln(d)In the examples of this section we have used Mosel’s standard text format for reading and writing
dates and times. These data types can also be used when accessing spreadsheets or databases
through Mosel’s ODBC connection or the software-speciﬁc interfaces for Oracle and MS Excel. The
whitepaper Using ODBC and other database interfaces with Mosel documents some examples ofaccessing date and time data in spreadsheets and databases.
Note: When initializing or constructing dates Mosel does not control whether they correspond to an
actual calendar day (e.g., 0-valued or negative day and month counters are accepted). The validity of adate or time can be tested with the function isvalid. For example, the following code extract

Fair Isaac Corporation Conﬁdential and Proprietary Information

193

Language extensions
d:= date(2000,0,0)
writeln(d, " is a valid date: ", if(isvalid(d), "true", "false"))results in this output:
2000-00-00 is a valid date: false

17.5.2

Dates and times as constants
It is possible to use the types ’date’, ’time’, ’datetime’ as index sets for arrays if the elements of the set
are ﬂagged as being constant. The effect of such a declaration as constant is illustrated by thefollowing code snippet (taken from the example ﬁle dates.mos). Entities such as someday in theexample below that receive a value directly in the declarations block via ’=’ also are constants, theirvalue can not be changed or re-assigned.
declarations
someday=date(2020,3,24)
SD: set of date
SDC: set of constant date
AD: dynamic array(SDC) of real
end-declarations! A constant date
! Set of date references! Set of constant date references! Array indexed by 'date' type

! Operations on a set of dates
SD:= {date(2020,3,24), date(2020,3,24)+1}
writeln("Is someday in SD? ", someday in SD)
writeln("Next day in SD? ", someday+1 in SD)
SD+= {date(2020,3,24), date(2020,3,24)+1}
writeln("SD after addition: ", SD, " size=", SD.size)! Output: false! Output: false! Output: size=4! Operations on a set of constant dates
SDC:= {date(2020,3,24),date(2020,3,24)+1}
writeln("Is someday in SDC? ", someday in SDC)
! Output: true
writeln("Next day in SDC? ", someday+1 in SDC)
! Output: true
SDC+= {date(SYS_NOW), date(SYS_NOW)+1}
writeln("SDC after addition: ", SDC, " size=", SDC.size) ! Output: size=2

The example shown here only mentions the type ’date’, but the constant declaration is also applicableto the types ’time’ and ’datetime’.

17.5.3

Conversion to and from numbers
In some cases it might be necessary to use the numerical representation in the place of a date or time.
In the following Mosel extract we wish to deﬁne an array YEARS that is indexed by a set of dates. In thisexample we show how to use as index values the numerical representation that is obtained by applying
getasnumber to the dates (this function returns an integer, the Julian Day number = number of days
elapsed since 1/1/1970; if the argument is a time getasnumber returns the number of milliseconds
since midnight). By applying date to the numerical representation it is converted back to the originaldate.
With this Mosel code
declarations
Dates: set of date
YEAR: array(NDates: set of integer) of integer
end-declarations
setparam("datefmt", "")! Use the default format

initializations from "datetime.dat"
Dates
end-initializations

Fair Isaac Corporation Conﬁdential and Proprietary Information

194

Language extensions

writeln("Dates: ", Dates)
forall(dd in Dates) YEAR(getasnumber(dd)):= getyear(dd)writeln("YEAR: ", YEAR)
forall(n in NDates) writeln(date(n))! Mapping back to original dates

and the following data
Dates: [ "1999-1-21" "2000-2-22" "2002-3-23" "2005-4-24" "2010-5-25"]

we obtain this output:
Dates: {1999-01-21,2000-02-22,2002-03-23,2005-04-24,2010-05-25}
YEAR: [(10612,1999),(11009,2000),(11769,2002),(12897,2005),(14754,2010)]
1999-01-21
2000-02-22
2002-03-23
2005-04-24
2010-05-25

Similarly to what is shown here, function getasnumber can be used with ’time’ and ’datetime’, thebackwards conversion being carried out by time or datetime respectively.

17.5.4

Operations and access functions
The following Mosel model extract (dates.mos) shows some operations on dates and times,
including sorting lists of dates or times, difference between two dates or times, and addition of
constants to obtain an enumeration of dates or times.
declarations
t: timed: datenow1,now2: datetime
DNAMES: array(1..7) of string
TList: list of time
DList: list of dateend-declarations! Difference between dates
writeln("February 2004 had ", date(2004,3,1)-date(2004,2,1), " days.")! Retrieve the weekday
DNAMES:: (1..7)["Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday", "Sunday"]writeln("1st January 2000 was a ", DNAMES(getweekday(date(2000,1,1))))! Difference between times
now1:= datetime(SYS_NOW)
wait(1)! Delay model execution for 1 second
now2:= datetime(SYS_NOW)
writeln("Elapsed time: ", now2-now1, "ms")! Enumeration / addition to 'time'
setparam("timefmt", "%.h.%0M%p")t:= time(11,0)
forall(i in 1..5) dowriteln(t)t+=30⁎60⁎1000
end-do! Add 30 minutes! Enumeration / addition to 'date'setparam("datefmt", "%.d/%0m/%0Y")
d:= date(2005,12,20)
forall(i in 1..5) dowriteln(d)Fair Isaac Corporation Conﬁdential and Proprietary Information

195

Language extensionsd+=14
end-do! Add 14 days

! Sorting lists of dates and times
setparam("datefmt", "")! Revert to default date format
DList:= [date(2021,1,1),date(1900,1,2),date(2020,3,24)]
writeln("Orig. DL=", DList)
qsort(SYS_UP, DList)
writeln("Sorted DL=", DList)
setparam("timefmt", "")! Revert to default time format
TList:= [time(12,0),time(10,30),time(16,15),time(8,45)]
writeln("Orig. TL=", TList)
qsort(SYS_UP, TList)
writeln("Sorted TL=", TList)

Executing this model produces the following output.
February 2004 had 29 days.
1st January 2000 was a Saturday
Elapsed time: 1.006ms
11.00am
11.30am
12.00pm
12.30pm
1.00pm20/12/05
3/01/06
17/01/06
31/01/06
14/02/06
Orig. DL=[2021-1-1,1900-01-02,2020-03-24]
Sorted DL=[1900-01-02,2020-03-24,2021-1-1]
Orig. TL=[12:00:00,10:30:00,16:15:00,8:45:00]
Sorted TL=[8:45:00,10:30:00,12:00:00,16:15:00]

17.6

Text handling and regular expressions
The module mmsystem provides a large set of text handling functionality, including

the types text, parsectx, and textareatext formatting routines (number format, upper/lower case)parsing routinesregular expressions

In the following subsections we show some examples of text handling with Mosel, for a full descriptionof the available functinoality please refer to the chapter mmsystem of the ’Mosel Language Reference
Manual’.

17.6.1text vs. string
Although apparently denoting similar concepts, the purpose and usage recommendations for the types
string and text in Mosel models are quite distinct: any string deﬁned in a model is added to themodel’s names dictionary and is only freed at termination of the model run, this is not the case formodel objects of the type text. The type string therefore should be used whenever it is a questionof identifying objects, so in particular for index sets.

Fair Isaac Corporation Conﬁdential and Proprietary Information

196

Language extensions

The type text is in general the more appropriate choice for descriptive or editable texts, including
reporting or logging messages, and any texts generated via (partial) copies or concatenation. A text
object can be altered, allowing for a considerably wider set of operations (such as insertion, deletion) incomparison with strings. Furthermore, with the I/O driver text: a public text object can be used as
input or output ﬁle in a model (see Section 17.1.2).
It is, however, not always possible to draw a clear line between where to use string or text. A
number of module subroutines therefore deﬁne multiple versions, accepting both, string or text
arguments. Note further that if required, Mosel automatically converts from the type string to text,
but not the other way round.

17.6.2

Parsing text
In the example below we conﬁgure the global parser settings to read real numbers from a text that has
ﬁelds separated by commas.
declarations
values: list of realcomma=getchar(",",1)
end-declarations! ASCII value for ","txt:= text(", , 123.4 , 345.6 ,")! Parsing without context
setparam("sys_sepchar", comma)! Comma as separation character
setparam("sys_trim", true)! Trim blanks around separation character
while (nextfield(txt)) do! Get next fieldvalues+= [parsereal(txt)]! Read a real number from the field
writeln("Read up to position ", getparam("sys_endparse"))end-dowriteln("Values read: ", values)! Output: [0,0,123.4,345.6,0]

The same behavior can be achieved with a parser context—here we do not modify any global settings,
which has the advantage of preventing possible interactions with other parser settings that may be
used elsewhere in our model.
declarations
pctx: parsectx
values: list of realcomma=getchar(",",1)
end-declarations! ASCII value for ","txt:= text(", , 123.4 , 345.6 ,")! Parsing real numbers with context
setsepchar(pctx, comma)! Comma as separation character
settrim(pctx, true)! Trim blanks around separation character
while (nextfield(txt,pctx)) do! Get next fieldvalues+= [parsereal(txt, pctx)]! Read a real number from the field
writeln("Read up to position ", pctx.endparse)
end-dowriteln("Values read: ", values)! Output: [0,0,123.4,345.6,0]When implementing data handling for optimization applications, it is good practice to add error
handling to the parsing loop, for example to check whether the ﬁelds are formatted as expected:
pctx.endparse:=0! Start at the beginning of text
pctx.sepchar:=comma! Comma as separation character
pctx.trim:=true! Trim blanks around separation character
while (nextfield(txt,pctx)) do! Get next field
if getchar(txt, pctx.endparse)=comma or pctx.endparse>=txt.size thenvalues+=[0.0]! The field is empty
else

Fair Isaac Corporation Conﬁdential and Proprietary Information

197

Language extensions
r:=parsereal(txt, pctx)! Read a real number from the field
if getsysstat=0 then values+= [r]elsewriteln("Malformed field contents at position ", pctx.endparse," (", copytext(txt, pctx.endparse,pctx.endparse+2), ")")
end-ifend-ifwriteln("Read up to position ", pctx.endparse)
end-dowriteln("Values read: ", values)! Output: [0,0,123.4,345.6,0]

One might also choose to work with multiple parser contexts (e.g. using an ’inner’ context pctxi forreading some part of each ﬁeld from the original text—here an integer number that is read from a string
containing a real).
declarations
pctx,pctx1: parsectx
ivalues: list of integercomma=getchar(",",1)
end-declarations! ASCII value for ","txt:= text(", , 123.4 , 345.6 ,")setsepchar(pctx, comma)! Comma as separation character
settrim(pctx, true)! Trim blanks around separation character
while (nextfield(txt,pctx)) do! Get next field
tt:=parsetext(txt, pctx)! Get contents of the field
pctxi.endparse:=1! Reset start to beginning of the text
i:=parseint(tt,pctxi)! Read an integer number from the field
if getsysstat=0 then ivalues+= [i]; end-if
writeln("Read up to position ", pctx.endparse)
end-dowriteln("Values read: ", ivalues)! Output: [123,345]

17.6.3

Regular expressions
A regular expression (in the following abbreviated to regex) is a sequence of characters that form asearch pattern. Regex are used to describe or match a set of strings according to certain syntax rules.
Mosel supports the Basic Regular Expressions syntax (BRE) and the Extended Regular Expressions
syntax (ERE) of the POSIX standard, the implementation of regular expression matching relies on the
TRE library.
Here are some examples of regular expression matching and replacement with some explanations of
the meaning of the employed regex—for a complete description of the supported regex syntax thereader is refered to the documentation of the TRE library (see http://laurikari.net/tre), another useful
resource are the examples provided on the page en.wikipedia.org/wiki/Regular_expression.
The following example (regex.mos) displays all strings containing ’My’ that occur in a text. The ﬁrst
matching statement uses BRE syntax, it displays all strings starting with ’My’ irrespective of
upper/lower case spelling (option REG_ICASE). The second matching statement uses ERE syntax(option REG_EXTENDED) to retrieve all strings containing ’My’ other than at their beginning. We havechosen to retrieve different individual portions of the matching string (speciﬁed via the parantheses in
the regular expression statement) the positions of which are stored in their order of occurrence into thearray m (of type textarea)
declarations
m: array(range) of textarea
t: text
end-declarations
t:="MyValue=10,Sometext Mytext MoretextMytext2, MYVAL=1.5 mYtext3"
m(0).succ:=1
while (regmatch(t, '\<My\(\w⁎\)', m(0).succ, REG_ICASE, m))writeln("Word starting with 'My': ", copytext(t,m(0)))

Fair Isaac Corporation Conﬁdential and Proprietary Information

198

Language extensions! Output:

MyValue

MytextMYVAL

mYtext3m(0).succ:=1
while (regmatch(t, '\w+((My)(\w⁎))', m(0).succ, REG_ICASE+REG_EXTENDED, m))writeln("String containing 'My' (not at beginning): ",copytext(t,m(0)), " (", copytext(t,m(1)), "=", copytext(t,m(2)) ,"+", copytext(t,m(3)), ")")! Output: MoretextMytext2 (Mytext2=My+text2)

The special characters used in the formulation of the regular expressions above have the following
meaning: \< marks the beginning of a word, \w denotes alphanumeric or underscore characters, ⁎means 0 or more times and + stands for 1 or more times.
The following Mosel code snippet shows how to replace matching expressions in a text that contains
dates with different formats:t:="date1=20/11/2010,date2=1-Oct-2013,date3=2014-6-30"
numr:= regreplace(t, '([[:digit:]]{4})-([01]?[[:digit:]])-([0-3]?[[:digit:]])','\3/\2/\1', 1, REG_EXTENDED)
if numr>0 thenwriteln(numr, " replacements: ", t)
end-ifThis is the output produced by the code above:
1 replacements: date1=20/11/2010,date2=1-Oct-2013,date3=30/6/2014

There are alternative ways of stating the same regular expression with BRE or ERE syntax, for example:
numr:= regreplace(t, '\(\d\{4\}\)-\([01]\{0,1\}\d\)-\([0-3]\{0,1\}\d\)','\3/\2/\1' )numr:= regreplace(t, '(\d{4})-([01]{0,1}\d)-([0-3]{0,1}\d)','\3/\2/\1', 1, REG_EXTENDED )In these replacement statements we have used the following special characters for stating regularexpressions: \d or [:digit:] indicates a numerical character, square brackets contain a set of
possible character matches, {M,N} means minimum M and maximum N match count and ? stands for
0 times or once.

Fair Isaac Corporation Conﬁdential and Proprietary Information

199

CHAPTER 18

Annotations
Annotations are meta data expressed in a Mosel source ﬁle (model or package) that are stored in the
resulting BIM ﬁle after compilation. This additional information is either global or associated withpublic globally declared objects (including subroutines). Annotations do not have any direct impact on
the model itself as they are treated like comments. Typical uses of annotations include model
documentation or application conﬁguration information.
Mosel annotations have the following format:
a single-line annotation starts with ’!@’ and a name!@doc.name This is my document titlemulti-line annotations are surrounded by ’(!@’ and ’!)’(!@mynote Some annotation text.
Another line of text.
!)’!@’ is followed by the annotation name (identiﬁer)– no space between ’!’ and ’@’ characters– space is allowed between ’@’ and the name

value assignment operators are ’ ’ (space), ’:’, or ’=’– no space between annotation name and operator!@mynote contents for 'mynote'!@ another=contents of 'another'!@third: contents of 'third'association with symbols:!@mynote This annotation is applied to all objects declared below
public declarations
val: integer!@doc.descr Explanation of 'val'!@doc.descr Text associated with 'msg'
msg: string! A standard comment!@mynote2 This annotation is ignored (no associated object)
end-declarations

Annotations are organized in categories. A category groups a set of annotations and other categories(or sub-categories). For example
doc.name

Fair Isaac Corporation Conﬁdential and Proprietary Information200

Annotations

will be used to select the annotation name member of the doc category. Predeﬁned category names
include mc (Mosel compiler) and doc (model documentation). Models can also deﬁne/employ new
annotations and categories—these must be valid Mosel identiﬁers, that is, their names can only usealpha-numeric symbols and ’_’.
We show some examples of the doc category in Section 18.2 below. The category mc is used to pass
information to the compiler during the compilation, including the (optional) declaration of new
annotations with the mc.def annotation or the deﬁnition of aliases, such as!@mc.def descr alias doc.descr insight.descr

that redirects onto two different annotations (see section ’Annotations’ of the Mosel Language
Reference Manual for further detail).

18.1

Accessing annotations
Annotations can be retrieved from the model itself during its execution or before/after execution fromthe calling program (using the Mosel libraries). The following example annottest.mos shows how toretrieve an annotation that is deﬁned in the same model. The subroutine getannotations is deﬁned
by mmjobs, it takes an additional ﬁrst argument of type Model if it is to be applied to a submodel andnot the model itself.
model "Using annotations"uses "mmjobs"
public declarations(!@.
@value.first 5@value.second 0@descr A scalar value!)
myint: integer
AnnNames: set of string
Ann: array(string) of string
mytxt: text
end-declarations!@descr Set of annotation names!@descr Annotation values!@descr Default input data file!@descr Annotations test!@furtherinfo Simply displays all defined global or specific annotations! Get all global annotations defined in this model:
getannotations("", "", AnnNames, Ann)
writeln("Global annotations:")
forall(a in AnnNames) writeln(" ", a, " = ", Ann(a))! Get all annotations for "myint":
getannotations("myint", "", AnnNames, Ann)
writeln("Annotations defined for 'myint':")forall(a in AnnNames) writeln(" ", a, " = ", Ann(a))! Retrieve all annotations starting with 'value.' and that are! associated to 'myint'
getannotations("myint", "value.", AnnNames, Ann)
writeln("'value' annotations for 'myint':")forall(a in AnnNames) writeln(" ", a, " = ", Ann(a))
end-model

Running this model produces output like the following:
Global annotations:.descr = Annotations test

Fair Isaac Corporation Conﬁdential and Proprietary Information

201

Annotations.furtherinfo = Simply displays all defined global or specific annotations
Annotations defined for 'myint':.descr = A scalar value
value.first = 5
value.second = 0'value' annotations for 'myint':
value.first = 5
value.second = 0

And here is an example how to retrieve annotations into a C program (ﬁle annotdisplay.c):#define MAXANN 100
int main(){
XPRMmodel mod;void ⁎ref;const char ⁎symb;
const char ⁎ann[MAXANN⁎2];
int i,n;/⁎ A model object name ⁎//⁎ List of annotations ⁎/if(XPRMinit()) return 1;/⁎ Initialize Mosel ⁎/if((mod=XPRMloadmod("annottest.bim",NULL))==NULL) /⁎ Load a BIM file ⁎/return 2;/⁎ Retrieve and display global annotations ⁎/n=XPRMgetannotations(mod,NULL,NULL,ann,MAXANN⁎2);
printf("Global annotations (total: %d):\n", n/2);
for(i=0;i<n && i<MAXANN;i+=2)
printf("%s:%s\n",ann[i],(ann[i+1]!=NULL)?ann[i+1]:"");/⁎ Retrieve and display all annotations associated with model objects ⁎/printf("Annotations associated with objects:\n");ref=NULL;
while((symb=XPRMgetnextanident(mod,&ref))!=NULL){
n=XPRMgetannotations(mod,symb,NULL,ann,MAXANN⁎2);
printf(" %s->\n",symb);for(i=0;i<n && i<MAXANN;i+=2)
printf("%s:%s\n",ann[i],(ann[i+1]!=NULL)?ann[i+1]:"");}/⁎ Retrieve and display annotations for model object 'myint' ⁎/n=XPRMgetannotations(mod,"myint",NULL,ann,MAXANN⁎2);
printf("Annotations defined for 'myint' (total: %d):\n", n/2);
for(i=0;i<n && i<MAXANN;i+=2)
printf("%s:%s\n",ann[i],(ann[i+1]!=NULL)?ann[i+1]:"");
return 0;}

The corresponding Java code looks as follows:
public class annotdisplay{
public static void main(String[] args) throws Exception{
XPRM mosel;
XPRMModel mod;
XPRMAnnotation ann[];// List of annotations
mosel = new XPRM();
mod = mosel.loadModel("annottest.bim");// Initialize Mosel// Load a BIM file// Retrieve and display global annotations
ann=mod.getAnnotations("");
System.out.println("Global annotations (total: "+ ann.length +"):");

Fair Isaac Corporation Conﬁdential and Proprietary Information

202

Annotations
for(int i=0;i<ann.length;i++) System.out.println(""+ann[i]);// Retrieve and display all annotations associated with model objects
System.out.println("Annotations associated with objects:");for(XPRMIdentifiers ids=mod.annotatedIdentifiers(); ids.hasNext();){
XPRMIdentifier id=(XPRMIdentifier)ids.next();
ann=mod.getAnnotations(id,"");
System.out.println(" "+id.getName()+"->");
for(int i=0;i<ann.length;i++) System.out.println(""+ann[i]);}// Retrieve and display annotations for model object 'myint'
ann=mod.getAnnotations("myint","");
System.out.println("Annotations defined for 'myint' (total: "+ ann.length +"):");
for(int i=0;i<ann.length;i++) System.out.println(""+ann[i]);}}Notice that the host application only needs to load the BIM ﬁle (and not necessarily run a model) inorder to be able to retrieve the annotations.

18.2 moseldoc: Generating model documentation
The Mosel compiler reserves a special treatment to annotations belonging to the doc category. This
annotation category will only be included into the BIM ﬁle if the source ﬁle is compiled with the option-D, such as
mosel comp -D mymodel.mos

To enable the -D compiler option in Xpress Workbench open the Run menu and select the entry
Compiler Options, enable the Generate Doc Annotations option, and conﬁrm with Save. When you next
in the Workbench toolbar, the resulting BIM ﬁle will contain documentation
click the Compile button
annotations.
The tool moseldoc can be applied to the resulting BIM ﬁle to generate an XML ﬁle that is thenprocessed into a set of HTML pages:
moseldoc mymodel
moseldoc -html mymodel
moseldoc -o mydir -html mymodel
moseldoc -xml mymodel
moseldoc -ixml mymodel
moseldoc -f mymodel! Generates HTML and XML! HTML output only
! Specify HTML output directory! XML output only
! XML file for inclusion (omitting header+root)! Force output overwrite

Here are some examples of how to use the documentation annotations:Document structure:(!@doc.
@title An example of model documentation@version 0.0.1@date March 2015
@chapter Introduction@pThis model needs to be compiled with the <tt>-D</tt> compiler option
to include the documentation annotations into the BIM.
The <tt>moseldoc</tt> program takes the resulting BIM file as input.
!)

Fair Isaac Corporation Conﬁdential and Proprietary Information203

Annotations!@doc.chapter The example!@doc.section Parameters

The resulting cover page and table of contents generated by moseldoc look as follows (thecontents listing also refers to the entity and subroutine annotations shown in the following items):Documenting parameters:
parameters!@doc.descr Short parameter description!@doc.value v1 possible value!@doc.value v2 another possible value!@doc.info Some more explanation (longer text)
MYPAR="some text"
end-parameters

Documenting entity declarations (type deﬁnitions, constants, variables):!@doc.section Constants and variables
public declarations(!@doc.
@descr Short description of the constant set@info Some additional information!)
S=1..10!@doc.descr An error code constant
MYERR=11(!@doc.
@descr A record type@recflddescr fld1 field description@recflddescr fld2 another field description@info Several @doc.info tags can be used for a given entity@info Entities can be referenced: <entRef>rectype</entRef>!)
rectype=public record
fld1:integer
fld2:string
end-record
end-declarations

This is the HTML page generated by moseldoc from these parameter and entity annotations:

Fair Isaac Corporation Conﬁdential and Proprietary Information

204

Annotations



Documenting subroutines:!@doc.section Subroutines(!@doc.
@descr A short description@param i first parameter@paramval i value1 description of value1@paramval i value2 description of value2@param r second parameter@err MYERR reference to an error code constant@return The return value@example Some descriptive text for the example@example [SRC]
the example code is here
@info Some useful information for <tt>myfunct</tt>@related <fctRef>myotherfunct</fctRef>!)
public function myfunct(i:integer,r:real):boolean
returned:=i>r
end-function!@doc.group myfunct!@doc.info <tt>myotherfunc</tt> is an alternative to <fctRef>myfunct</fctRef>
public function myotherfunc(i:integer):boolean
returned:=true
end-function

The HTML subroutine documentation page generated by moseldoc from these annotations isshown here:

Fair Isaac Corporation Conﬁdential and Proprietary Information

205

Annotations



Relocating documentation contents:!@doc.section A section title!@doc.relocate newlocref...
! All doc annotations defined here will be inserted at marker 'newlocref'!@doc.relocate...
! All subsequently defined doc annotations remain where they are!@doc.section Destination location!@doc.location newlocrefExcluding contents from generated documentation:!@doc.autogen=false
public declarations... ! All doc annotations defined here will be ignored
end-declarations!@doc.autogen=true... ! All subsequently defined doc annotations will get processed

Fair Isaac Corporation Conﬁdential and Proprietary Information

206

V. Remote invocation of Mosel

Overview
All previous sections of this manual assume that you are working with a standard installation of Xpress
on your local computer. However, a local installation of Xpress is not a requirement when working with
Mosel. The examples in this part show how to use the Mosel remote invocation library XPRD forbuilding applications requiring the Xpress technology that run from environments where Xpress is notinstalled—including architectures for which Xpress is not available.
XPRD is a self-contained library (i.e. with no dependency on the usual Xpress libraries) that relies on the
Mosel Distributed Framework (module mmjobs, see Section 17.2). The examples in this part are
introductory examples of some of the most commun programming tasks when working with a remote
installation of Xpress, namelystarting Mosel instances (locally or on remote hosts)

compiling, loading, running, and interrupting Mosel models remotely

redirection of standard streams

sending and receiving events

retrieving data from a Mosel model

Further examples, particularly of more advanced uses, are discussed in the whitepaper Multiple models
and parallel solving with Mosel and also in the Advanced Evaluators’ Guide. Both documents areprovided with their examples as a part of the Xpress distribution. For a complete documentation of the
XPRD library the reader is referred to the XPRD Reference Manual.
The ﬁrst chapter (Chapter 19) of this part introduces the C version of XPRD. The Java versions of thesame examples are described in Chapter 20.

Fair Isaac Corporation Conﬁdential and Proprietary Information

208

CHAPTER 19

XPRD CThe program example runprimedistr.c below shows how to run the model prime.mos remotelyusing the XPRD C library (NB: this program corresponds to the mmjobs distributed computing example
runprimedistr.mos from Section 17.2.4). At ﬁrst sight, the reader might be reminded of the Mosel C
libraries presented in Chapter 13. However, there are two major additions besides the change of thepreﬁxes from XPRM to XPRD:

We need to connect to a remote machine and create a new Mosel instance (XPRDmosel) prior toworking with any Mosel models. Remote machines are speciﬁed by their name or IP address, theempty string in the present example indicates that we want to use the local machine.
The submodel is executed in an independent process and we therefore need to wait for its
termination.

In summary, the standard execution sequence for Mosel models of compile load run is augmented toconnect – compile load run wait in the context of distributed computing (this remark equally applies tosubmodels launched via mmjobs).
Instead of simply waiting for the submodel to terminate, the program below waits for 2 seconds and if
no termination event message has been received from the Mosel model, it is stopped by theapplication. After termination of the submodel (either by ﬁnishing its calculations within less than 2seconds or stopped by the master model) the application reports the full event information and also
displays the termination status and the exit value of the Mosel model. Unloading a model explicitly as
shown here is only really necessary in larger applications that continue after the termination of thesubmodel, so as to free the memory used by it.
#include <stdio.h>#include <stdlib.h>#include "xprd.h"
int main(int argv,char ⁎args[]){
XPRDcontext xprd;
XPRDmosel moselInst;
XPRDmodel modPrime, evsender;
double evvalue;
int evclass;
xprd=XPRDinit();/⁎ Create an XPRD context ⁎//⁎ Open connection to a remote node: "" means the node running this program ⁎/moselInst=XPRDconnect(xprd, "", NULL, NULL, NULL, 0);/⁎ Compile the model file ⁎/XPRDcompmod(moselInst, "", "rmt:prime.mos", "rmt:prime.bim", "");/⁎ Load BIM into the remote instance ⁎/modPrime=XPRDloadmod(moselInst, "rmt:prime.bim");
XPRDrunmod(modPrime, "LIMIT=50000");

Fair Isaac Corporation Conﬁdential and Proprietary Information/⁎ Start execution and ⁎/209

XPRD CXPRDwaitevent(xprd,2);/⁎ wait 2 seconds for an event ⁎/if (XPRDqueueempty(xprd)==1) /⁎ No event has been sent... ⁎/{printf("Model too slow: stopping it!\n");XPRDstoprunmod(modPrime);/⁎ ... stop the model, then wait ⁎/XPRDwaitevent(xprd,-1);}
XPRDgetevent(xprd, &evsender, &evclass, &evvalue);/⁎ Get the event ⁎/printf("Event value: %g sent by model %d\n", evvalue, XPRDgetnum(evsender));
printf("Exit status: %d\n", XPRDgetstatus(modPrime));printf("Exit code : %d\n", XPRDgetexitcode(modPrime));}

XPRDunloadmod(modPrime);
XPRDdisconnect(moselInst);
XPRDfinish(xprd);/⁎ Unload the model ⁎//⁎ Disconnect remote instance ⁎//⁎ Terminate XPRD ⁎/remove("prime.bim");/⁎ Clean up temporary files ⁎/return 0;

In this example, we assume that the model source is saved on the local machine running XPRD, and theBIM ﬁle is written back to this machine (indicated by the I/O driver preﬁx rmt: in the ’compile’ and’load’ functions that are executed on the remote instance). Alternatively, we might choose to save theBIM ﬁle on the remote machine, e.g. in memory (shmem:primebim) or in Mosel’s temporary directory(tmp:prime.bim).

19.1

Exchanging data with the model
A typical programming task when working with remote models is the retrieval of results into the calling
application. In this section we show how to use XPRD functionality for retrieving data that is written by
a Mosel model in an initializations block into the XPRD program. The choice of the method forexchanging data usually depends on the particular system setup (write access rights) and the volumeof data to be communicated (memory usage). Data in Mosel format can be output
1. on the remote machine running Mosel(a) in memory(b) as a physical ﬁle2. on the local machine running XPRD(a) in memory(b) as a physical ﬁleCase 1a is implemented in the program version runprimeiodistr.c printed below. Case 1b is
obtained by removing the shmem: preﬁx from the ﬁle name, for example, the setting
OUTPUTFILE=bin:tmp:resdata

will create a ﬁle resdata in Mosel’s temporary directory. Cases 2a and 2b could use the setting
OUTPUTFILE=bin:rmt:resdata

For the implementation of case 2b we simply replace the calls to the XPRD remote ﬁle accessfunctions by the standard C library functions fopen, fread, and fclose. Somewhat more work is

Fair Isaac Corporation Conﬁdential and Proprietary Information

210

XPRD Crequired for the implementation of case 2a: the program needs to deﬁne an XPRD ﬁle manager tohandle the data in memory—an example implementation is provided in the ﬁle runprimeiodistr2.c.
All implementation versions share the use of the bin: I/O driver and they all deﬁne the same function
show_solution that decodes Mosel’s binary format and displays the solution values. Using Mosel’sbinary format is recommended (though not a necessity) in distributed applications—it isplatform-independent and uses less space than the standard text format.
The program example of the previous section stops the Mosel model with a call to XPRDstoprunmod.
We now replace this hard stop by sending the user event ‘STOPMOD’ to the submodel: instead ofimmediately terminating the submodel this event is intercepted by the submodel and makes it interrupt
its calculations and write out the current solution. To make sure that the submodel is actually runningat the point where we sent the ‘STOPMOD’ event, we have also introduced a ’MODREADY’ event sentfrom the submodel to the master to indicate the point of time when it starts the calculations (withheavy operating system loads the actual submodel start may be delayed).
The Mosel model primeio.mos remains the same as shown in Section 17.2.3.
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "xprd.h"#include "bindrv.h"#define STOPMOD 2#define MODREADY 3/⁎ Identifier for "Stop submodel" user event ⁎//⁎ Identifier for "Submodel ready" user event ⁎/int main(int argv,char ⁎args[]){
XPRDcontext xprd;
XPRDmosel moselInst;
XPRDmodel modPrime, evsender;
double evvalue;
int evclass;
XPRDfile f;
xprd=XPRDinit();/⁎ Create an XPRD context ⁎//⁎ Open connection to a remote node:"" means the node running this program ⁎/moselInst=XPRDconnect(xprd, "", NULL, NULL, NULL, 0);/⁎ Compile the model file ⁎/XPRDcompmod(moselInst, "", "rmt:primeio.mos", "tmp:primeio.bim", "");/⁎ Load the bim file into the remote instance ⁎/modPrime=XPRDloadmod(moselInst, "tmp:primeio.bim");/⁎ Disable submodel output ⁎/XPRDsetdefstream(moselInst, modPrime, XPRD_F_WRITE, "null:");/⁎ Start execution ⁎/XPRDrunmod(modPrime, "LIMIT=50000,OUTPUTFILE=bin:shmem:resdata");
XPRDwaitevent(xprd,0);/⁎ Wait for an event ⁎/XPRDgetevent(xprd, &evsender, &evclass, &evvalue);/⁎ Get the event ⁎/if (evclass != MODREADY)/⁎ Check the event class ⁎/{printf("Problem with submodel run");return 1;}
XPRDwaitevent(xprd,2);/⁎ Wait 2 seconds for an event ⁎/if (XPRDqueueempty(xprd)==1) /⁎ No event has been sent ⁎/{printf("Model too slow: stopping it!\n");XPRDsendevent(modPrime, STOPMOD, 0);/⁎ Stop the model, then
XPRDwaitevent(xprd,-1);/⁎ wait for its termination ⁎/}Fair Isaac Corporation Conﬁdential and Proprietary Information

⁎/

211

XPRD C/⁎ Open the output file, retrieve and display the solution data ⁎/f=XPRDfopen(moselInst, "shmem:resdata", XPRD_F_BINARY|XPRD_F_INPUT, NULL,0);
show_solution(my_read,f);
XPRDfclose(f);XPRDunloadmod(modPrime);
XPRDdisconnect(moselInst);
XPRDfinish(xprd);}/⁎ Unload the model ⁎//⁎ Disconnect remote instance ⁎//⁎ Terminate XPRD ⁎/return 0;

Once the submodel has terminated we read its solution from the location speciﬁed in the model
parameter OUTPUTFILE and display the results. The subroutine show_solution uses functions fromthe bindrv library that is provided with XPRD to decode Mosel’s binary format. The output ﬁle read bythis routine has the same structure as the correponding text ﬁle in Mosel format, for example:
NumP: 6SPrime: [ 2 3 5 7 11 13 ]Since XPRD and bindrv expect different signatures for their reading functions, we also deﬁne a wrapper
function my_read.
/⁎⁎⁎⁎ Wrapper function for 'bindrv' ⁎⁎⁎⁎/
static size_t my_read(void ⁎buf, size_t size, size_t nmemb, void ⁎ctx){
size_t s,a;long t;s=size⁎nmemb;a=0;
while(s>0){
t=XPRDfread(ctx,(char⁎)buf+a,s);if(t<=0) break;
else
{
a+=t;
s-=t;
}
}
return a/size;}/⁎⁎⁎⁎ Using bindrv: Decode the binary file and display its contents ⁎⁎⁎⁎/
void show_solution(size_t (⁎doread)(void ⁎,size_t,size_t,void⁎), void ⁎rctx){
s_bindrvctx bdrv;
int ⁎solarr;
int size,i,n;char ⁎str;
bdrv=bindrv_newreader(doread,rctx);/⁎ Initialize binreader ⁎/i=size=0;
solarr=NULL;
while(bindrv_nexttoken(bdrv)>=0){
bindrv_getctrl(bdrv,&n);/⁎ 'label' (marker) ⁎/bindrv_getstring(bdrv,&str);/⁎ Read a string ⁎/if(strcmp(str,"NumP")==0){
free(str);
bindrv_getint(bdrv,&size);/⁎ Read an integer ⁎/printf("( %d prime numbers)\n", size);if(size>0)/⁎ Prepare array to receive values ⁎/Fair Isaac Corporation Conﬁdential and Proprietary Information

212

XPRD C
solarr=malloc(sizeof(int)⁎size);else
break;}else
if(strcmp(str,"SPrime")==0){
free(str);
bindrv_getctrl(bdrv,&n);/⁎ [ (start marker) ⁎/while(bindrv_nexttoken(bdrv)==BINDRV_TYP_INT){/⁎ Read integers ⁎/bindrv_getint(bdrv,&(solarr[i++]));}
bindrv_getctrl(bdrv,&n);/⁎ ] (end marker) ⁎/}else{
printf("Unexpected label: %s\n", str);
free(str);
exit(1);}}bindrv_delete(bdrv);/⁎ Release bin reader ⁎//⁎ Print the set of prime numbers ⁎/printf("Prime numbers={");
for(i=0;i<size;i++)printf(" %d",solarr[i]);printf("}\n");free(solarr);}

Fair Isaac Corporation Conﬁdential and Proprietary Information/⁎ Clean up ⁎/213

CHAPTER 20

XPRD JavaFor the remote execution of Mosel models we need to augment the standard execution sequence for
Mosel models (that we have seen, for example, in Section 14.1) of compile load run to the sequenceconnect – compile load run wait (this remark equally applies to submodels launched via mmjobs). The
meaning of these additions is the following:

We need to connect to a remote machine and create a new Mosel instance (XPRDmosel) prior toworking with any Mosel models. Remote machines are speciﬁed by their name or IP address, theempty string in the example below indicates that we want to use the local machine.
The submodel is executed in an independent process and we therefore need to wait for its
termination.

The program example runprimedistr.java below shows how to run the model prime.mos using
the XPRD Java library. If the submodel has not terminated after 2 seconds (i.e., not termination
message has been received from this model), then it is stopped by the application. After termination of
the submodel (either by ﬁnishing its calculations within less than 2 seconds or stopped by the mastermodel) the application reports the full event information and also displays the termination status andthe exit value of the Mosel model. Unloading a model explicitly as shown here is only really necessary
in larger applications that continue after the termination of the submodel, so as to free the memory
used by it.
import com.dashoptimization.⁎;import java.lang.⁎;
import java.io.⁎;public class runprimedistr{public static void main(String[] args) throws Exception{
XPRD xprd=new XPRD();
XPRDMosel moselInst;
XPRDModel modPrime;
XPRDEvent event;moselInst=xprd.connect("");// Open connection to remote nodes// "" means the node running this program// Compile the model file on remote instancemoselInst.compile("", "rmt:prime.mos", "rmt:prime.bim");// Load the bim file into remote instancemodPrime=moselInst.loadModel("rmt:prime.bim");
modPrime.execParams = "LIMIT=50000";
modPrime.run();// Start execution and
xprd.waitForEvent(2);// wait 2 seconds for an event

Fair Isaac Corporation Conﬁdential and Proprietary Information

214

XPRD Javaif (xprd.isQueueEmpty())// No event has been sent...
{
System.out.println("Model too slow: stopping it!");modPrime.stop();// ... stop the model, then wait
xprd.waitForEvent();}// An event is available: model finishedevent=xprd.getNextEvent();
System.out.println("Event value: " + event.value +" sent by model " + event.sender.getNumber());
System.out.println("Exit status: " + modPrime.getExecStatus());
System.out.println("Exit code : " + modPrime.getResult());
moselInst.unloadModel(modPrime);
moselInst.disconnect();// Unload the submodel// Terminate the connection

new File("prime.bim").delete();}}// Clean up temporary files

The model source ﬁle prime.mos used by this example is saved on the local machine running XPRD,
and the BIM ﬁle is written back to this machine (indicated by the I/O driver preﬁx rmt: in the ’compile’
and ’load’ function calls that are executed on the remote instance). Alternatively, we might choose tosave the BIM ﬁle on the remote machine, e.g. in memory (shmem:primebim) or in Mosel’s temporary
directory (tmp:prime.bim).

20.1

Exchanging data with the model
An application that processes a Mosel model typically needs to retrieve some (result) data from themodel for reporting or further treatment. Besides exchanging data via external sources (e.g.
databases), Mosel offers a number of possibilities for directly retrieving data into an XPRD program.
The choice of the method for exchanging data usually depends on the particular system setup (write
access rights) and the volume of data to be communicated (memory usage). Data in Mosel format
written in an initializations block can be output
1. on the remote machine running Mosel(a) in memory(b) as a physical ﬁle2. on the local machine running XPRD(a) in memory(b) as a physical ﬁleCase 1a is implemented in the program version runprimeiodistr.java printed below. Case 1b is
obtained by removing the shmem: preﬁx from the ﬁle name, for example, the setting
OUTPUTFILE=bin:tmp:resdata

will create a ﬁle resdata in Mosel’s temporary directory. Cases 2a and 2b could use the setting
OUTPUTFILE=bin:rmt:resdata

For the implementation of case 2b we simply replace the XPRD remote ﬁle access by standard Java ﬁleaccess, for example

Fair Isaac Corporation Conﬁdential and Proprietary Information

215

XPRD Java
resdata=new FileInputStream("resdata");

Somewhat more work is required for the implementation of case 2a: the program needs to deﬁne an
XPRD ﬁle manager to handle the data in memory—an example implementation is provided in the ﬁlerunprimeiodistr2.java.
All implementation versions share the use of the bin: I/O driver and they all deﬁne the same function
showSolution that decodes Mosel’s binary format and displays the solution values. Using Mosel’sbinary format is recommended (though not a necessity) in distributed applications—it isplatform-independent and uses less space than the standard text format.
The program version printed below introduces two user events to achieve more precise time measures
for the remote process: the hard stop of the Mosel model is replaced by sending the user event‘STOPMOD’ to the submodel: instead of immediately terminating the submodel this event is interceptedby the submodel and makes it interrupt its calculations and write out the current solution. To make surethat the submodel is actually running at the point where we sent the ‘STOPMOD’ event, we have also
introduced a ’MODREADY’ event sent from the submodel to the master to indicate the point of time
when it starts the calculations (with heavy operating system loads the actual submodel start may bedelayed).
We work with the Mosel model primeio.mos from Section 17.2.3.
import com.dashoptimization.⁎;import java.lang.⁎;
import java.util.⁎;import java.io.⁎;public class runprimeiodistr{static final int STOPMOD = 2;
static final int MODREADY = 3;// Identifier for "Stop submodel" user event// Identifier for "Submodel ready" user event

public static void main(String[] args) throws Exception{
XPRD xprd=new XPRD();// Initialize XPRD
XPRDMosel moselInst;
XPRDModel modPrime;
XPRDEvent event;
InputStream resdata;
moselInst=xprd.connect("");// Open connection to remote nodes// "" means the node running this program// Compile the model file on remote instancemoselInst.compile("", "rmt:primeio.mos", "tmp:primeio.bim");// Load the bim file into remote instancemodPrime=moselInst.loadModel("tmp:primeio.bim");// Disable submodel output
modPrime.setDefaultStream(modPrime.F_OUTPUT, "null:");modPrime.execParams = "LIMIT=50000,OUTPUTFILE=bin:shmem:resdata";
modPrime.run();// Start execution and
xprd.waitForEvent();// ...wait for an event
event=xprd.getNextEvent();// Retrieve the event
if (event.eventClass != MODREADY)// Check the event class{
System.out.println("Problem with submodel run");System.exit(1);}xprd.waitForEvent(2);// Let the submodel run for 2 seconds

if (xprd.isQueueEmpty()){// No event has been sent...

Fair Isaac Corporation Conﬁdential and Proprietary Information

216

XPRD Java
System.out.println("Model too slow: stopping it!");modPrime.sendEvent(STOPMOD, 0);// ... stop the model, then
xprd.waitForEvent();// wait for its termination}// Open the output file, retrieve and display the solution data
resdata=moselInst.openForReading("shmem:resdata", moselInst.F_BINARY);
showSolution(resdata);
resdata.close();moselInst.unloadModel(modPrime);
moselInst.disconnect();}}// Unload the submodel// Terminate the connection

After termination of the model run, the XPRD application reads the solution data from the location
speciﬁed in the model parameter OUTPUTFILE and displays the results. Below follows theimplementation of the function showSolution that uses an instance of BinDrvReader to decode
Mosel’s binary format (the bindrv library is provided with XPRD). A binary format ﬁles has the samestructure as the correponding text ﬁle in Mosel format, for example:
NumP: 6SPrime: [ 2 3 5 7 11 13 ]Each data entry starts with a label (string followed by a colon), followed either by a single, scalar data
value, or a list of data values surrounded by square brackets.
// ⁎⁎⁎⁎ Decode the binary stream and display its contents ⁎⁎⁎⁎
static void showSolution(InputStream inbuf) throws Exception{
BinDrvReader bdrv=new BinDrvReader(inbuf); // Initialize binreader
String label;
ArrayList<Integer> setP=new ArrayList<Integer>();while(bdrv.nextToken()>=0){
bdrv.getControl();// 'label' (marker)
label=bdrv.getString();// Read a stringif(label.equals("NumP")){// Read an integer
System.out.println("(" + bdrv.getInt() + " prime numbers.)");}else
if(label.equals("SPrime")){bdrv.getControl();// [ (start marker)
while(bdrv.nextToken()==BinDrvReader.TYP_INT) // or ] at end of list{// Read integers
setP.add(new Integer(bdrv.getInt()));}
bdrv.getControl();// ] (end marker)}
else{
System.out.println("Unexpected label: "+label);
System.exit(0);}}// Display the contents of the set 'SPrime'
Iterator<Integer> iprime=setP.iterator();
System.out.print("Prime numbers={");
while(iprime.hasNext()){
Integer p=iprime.next();
System.out.print(" "+p);

Fair Isaac Corporation Conﬁdential and Proprietary Information

217

XPRD Java}
System.out.println(" }");}

Fair Isaac Corporation Conﬁdential and Proprietary Information

218

Appendix

APPENDIX A

Mosel Language overview
A.1

Structure of a Mosel model
A Mosel model (text ﬁle with extension .mos) has the form
model model_nameCompiler directives
Parameters
Body
end-model

Compiler directivesOptions are speciﬁed as a compiler directive, at the beginning of themodel
Options include explterm, which means that each statement must end
with a semi-colon, and noimplicit, which forces all objects to be
declared
options explterm
options noimplicit



uses statements are also compiler directives
uses "mmxprs", "mmodbc"

Can deﬁne a version number for your modelversion 1.0.0



Another set of compiler directives serves for the deﬁnition andconﬁguration of namespaces
namespace mynsp
nssearch myns2

Run-time parameters








Model body

Scalars (of type integer, real, boolean, or string) with a speciﬁed
default value
Their value may be reset when executing the model
Use initializations from for inputting structured data (arrays,
sets,...)
At most one parameters block per model
Model statements other than compiler directives and parameters,
including any number ofFair Isaac Corporation Conﬁdential and Proprietary Information

220

Mosel Language overview–––
Implicit declaration

Mosel statements

Mosel does not require all objects to be declared
Simple objects can be used without declaring them, if their type isobvious
Use the noimplicit option to force all objects to be declared before
using them (see item Compiler directives above)Can extend over several lines and use spaces

However, a line break acts as an expression terminator

A.2

declarations
initializations from / initializations tofunctions and procedures

To continue an expression, it must be cut after a symbol that implies
continuation (e.g. + - , ⁎ )Data structuresarray, set, list, record and any combinations thereof, e.g.,
S: set of list of integer
A: array(range) of set of real

ArraysArray: collection of labeled objects of a given type where the label of an array entry is
deﬁned by its index tuple
declarations
A: array(1..5) of real
B: array(range, set of string) of integer
x: array(1..10) of mpvar
end-declarations
A:: [4.5, 2.3, 7, 1.5, 10]
A(2):= 1.2
B:: (2..4,["ABC", "DE", "KLM"])[15,100,90,60,40,15,10,1,30]

Sets

Set: collection of objects of the same type without establishing an order among
them (as opposed to arrays and lists)
Set elements are unique: if the same element is added twice the set still only
contains it once.
declarations
S: set of string
R: range
end-declarations
S:= "A", "B", "C", "D"
R:= 1..10

Lists

List: collection of objects of the same typeA list may contain the same element several times. The order of the list elements is
speciﬁed by construction.
declarations
L: list of integer
M: array(range) of list of string
end-declarations
L:= [1,2,3,4,5]
M:: (2..4)[['A','B','C'], ['D','E'], ['F','G','H','I']]

Fair Isaac Corporation Conﬁdential and Proprietary Information

221

Mosel Language overview

Records

Record: ﬁnite collection of objects of any type
Each component of a record is called a ﬁeld and is characterized by its name and its
type.
declarations
ARC: array(ARCSET:range) of record
Source,Sink: string! Source and sink of arc
Cost: real
! Cost coefficient
end-record
end-declarations
ARC(1).Source:= "B"
ARC(3).Cost:= 1.5

User types

User types are treated in the same way as the predeﬁned types of the Mosel
language. New types are deﬁned in declarations blocks by specifying a typename, followed by =, and the deﬁnition of the type.
declarations
myreal = real
myarray = array(1..10) of myreal
COST: myarray
end-declarations

A.3

A.4

Selection statements
if ... end-if

if c=1 thenwriteln('c equals 1')
end-ifif ... else ... end-if

if c=1 thenwriteln('c equals 1')
else
writeln('c does not equal 1')
end-ifif ... elif ... else ... end-if

if c=1 thenwriteln('c equals 1')
elif c1 thenwriteln('c is bigger than 1')
else
writeln('c is smaller than 1')
end-ifcase ... end-case

case c of
1,2 : writeln('c equals 1 or 2')
3: writeln('c equals 3')
4..6: dowriteln('c is in 4..6')
writeln('c is not 1, 2 or 3')
end-doelse
writeln('c is not in 1..6')
end-case

Loops
forallforall(f in FAC, t in TIME)
make(f,t) = MAXCAP(f,t)
forall(t in TIME) douse(t) = MAXUSE(t)buy(t) = MAXBUY(t)end-doFair Isaac Corporation Conﬁdential and Proprietary Information

222

Mosel Language overview

while

i := 1
while (i = 10) do
write(' ', i)i += 1
end-dorepeat ... until

i := 1
repeatwrite(' ', i)i += 1
until (i 10)

break, next

counter

break jumps out of the current loop

break n jumps out of n nested loops (where n is a positive integer)next jumps to the beginning of the next iteration of the current loop

Use the construct as counter to specify a counter variable in abounded loop (i.e., forall or aggregate operators such as sum). At
each iteration, the counter is incremented
cnt:=0.0
writeln("Average of odd numbers in 1..10: ",(sum(cnt as counter, i in 1..10 | isodd(i)) i) / cnt)

A.5

Operators
Assignment operators

i := 10i += 20i -= 5! Same as i := i + 20
! Same as i := i - 5

Assignment operators with linear constraints
C := 5⁎x + 2⁎y = 20
D := C + 7⁎ythen D is
D := 5⁎x + 9⁎y - 20

The constraint type is dropped with :=C := 5⁎x + 2⁎y = 20
C += 7⁎ythen C is
C := 5⁎x + 9⁎y = 20

The constraint type is retained with +=, -=Arithmetic operators

standard:+ - ⁎ /
power:
ˆ
int. division/remainder: mod divsum:sum(i in 1..10) ...
product:prod(i in 1..10) ...
minimum/maximum:
min(i in 1..10) ...
count:
count(i in 1..10 | isodd(i))

Fair Isaac Corporation Conﬁdential and Proprietary Information

223

Mosel Language overview

Linear and non-linear expressions
Decision variables can be combined into linear or non-linear expressions
using the arithmetic operators

Logical operators

Set operators

Set comparison operators

List operators

A.6

module mmxprs only works with linear constraints, so no prod, min,max, ...
other solver modules, e.g., mmnl, mmxnlp, also accept (certain)
non-linear expressions

constants:
standard:
AND:
OR:
comparison:

true, false
and, or, notand(i in 1..10) ...
or(i in 1..10) ...
<, >, =, <>, <=, >=constants:
union:
union:
intersection:intersection:difference:{’A’, ’B’}+
union(i in 1..10) ...
⁎inter(i in 1..10) ...
-subset:superset:equals:not equals:
element of:
not element of:

Set1 <= Set2
Set1 >= Set2
Set1 = Set2
Set1 <>Set2"Oil5" in Set1
"Oil5" not in Set1

constants:
concatenation:
truncation:equals:not equals:
enumeration:[1, 2, 3]+, sum
L1 = L2
L1 <>L2i in L (within forall, sum etc.)

Built in functions and procedures
The following is a list of built in functions and procedures of the Mosel language (excluding modules).
Functions return a value; procedures do not.
Dynamic array handlingcreateexistsdelcell

isdynamic

Freeze (ﬁnalize) a dynamic set

finalize

Rounding functions

ceil

floor

round

abs

Mathematical functions

exp
cos
isodd

log
sin

ln
arctansqrt

Special real values

isfinite

isinf

isnan

Random number generator

randomsetrandseed

Fair Isaac Corporation Conﬁdential and Proprietary Information

224

Mosel Language overview

Minimum/maximum of a list of values

v := minlist(5, 7, 2, 9)
w := maxlist(CAP(1), CAP(2))

Inline “if” function

MAX_INVEN(t) := if(tMAX_TIME, 1000, 0)

Inven(t) := stock(t) = buy(t) - sell(t) +if(t 1, stock(t-1), 0)

Matrix export to ﬁleexportprob

File handlingfopen
fclosefselect
getfidgetfnamegetreadcnt
iseof
fflushfskipline
fwrite[_] / fwriteln[_]read / readln
write[_] / writeln[_]

String handlingstrfmtsubstr_Access and modify model objects

getcoeff[s]sethidden
gettypemakesos1
geteltfindlast
cutfirst
reversesetcoeff
ishiddensettypemakesos2
getfirst
getheadcutlastgetreverse

getvars
setnamegetsizesetrange

getlastgettailcutheadsplitheadfindfirst
cuteltcuttailsplittailgetobjval
getsolgetslack

getrcost
getactgetduallocalsetparam

Access solution values

Exit from a model

exit

Mosel controls

getparam

setparam

Date/timecurrentdate

currenttime

timestamp

Bit value handling

bitflip
bitshiftbitnegbittestbitsetbitvalMiscellaneous

asprocmemoryuse
resetversionnum

assertnewmuidsetioerr
versionstr

comparepublishsetmatherrrestoreparam

datablockunpublish

Overloading of subroutines– Some functions or procedures are overloaded: a single subroutine can be called withdifferent types and numbers of arguments



Additional subroutines are provided by Mosel library modules, which extend the basic Mosel
language, e.g.,– mmxprs: Xpress Optimizer– mmodbc: ODBC data connection– mmsheet: accessing spreadsheets– mmsystem: system calls; text handling– mmjobs: handling multiple models and (remote) Mosel instances– mmsvg: graphics⇒ See the ‘Mosel Language Reference Manual’ for full details

User-deﬁned functions and procedures– You can also write your own functions and procedures within a Mosel model

Fair Isaac Corporation Conﬁdential and Proprietary Information

225

Mosel Language overview– Structure of subroutines is similar to a model (may have declarations blocks)– User subroutines may deﬁne overloaded versions of built in subroutines⇒ See examples in Chapter Functions and procedures


Packages– Additional subroutines may also be provided through packages (Mosel libraries written in the
Mosel language as opposed to Mosel modules that are implemented in C)⇒ See the Chapter Packages for further detail

A.7

Constraint handling
Ctr1:= 2⁎x + y = 10
Ctr2:= x is_integer! Named constraints

2⁎x + y = 10
y = 5

! Anonymous constraints

Named constraints can beaccessed:val:= getact(Ctr)getvars(Ctr, vars)

hidden:redeﬁned:sethidden(Ctr, true)

modiﬁed:Ctr += 2⁎xsettype(Ctr, CT_UNB)

deleted (reset):

Ctr:= 0

Ctr:= x+y = 10
Ctr:= 2⁎x+5⁎y = 5

Anonymous constraints are constraints that are speciﬁed without assigning them to a linctr
variable. Bounds are (to Mosel) just simple constraints without a name. Anonymous constraints areapplied in the optimization problem just like ordinary constraints. The only difference is that it is notpossible to refer to them again, either to modify them, or to examine their solution value.

A.8

Problem handlingMosel can handle several problems in a given model ﬁle. A default problem is associated withevery model.
Built in type mpproblem to identify mathematical programming problems– The same decision variable (type mpvar) may be used in several problems– Constraints (type linctr) belong to the problem where they are deﬁnedThe statement with allows to open a problem (= select the active problem):declarations
myprob: mpproblem
end-declarations...
with myprob do
x+y = 0
end-do

Modules can deﬁne other speciﬁc problem types. New problem types can also be deﬁned by
combining existing ones, for instance:

Fair Isaac Corporation Conﬁdential and Proprietary Information

226

Mosel Language overview
mypbtyp = mpproblem and somepbtype
Problem types support assignment: P1:= P2
and additive assignment: P1 += P2

Fair Isaac Corporation Conﬁdential and Proprietary Information

227

APPENDIX B

Good modeling practice with Mosel
The following recommendations for writing Mosel models establish some guidelines as to how to write“good” models with Mosel. By “good” we mean re-usability, readability, and perhaps most importantly,
efﬁciency: when observing these guidelines you can expect to obtain the best possible performance ofMosel for the compilation and execution of your models.

B.1

Using constants and parameters
Many mathematical models start with a set of deﬁnitions like the following:
NT:= 3Months:= {'Jan', 'Feb', 'Mar'}MAXP:= 8.4
Filename= "mydata.dat"

If these values do not change later in the model, they should be deﬁned as constants, allowing Mosel tohandle them more efﬁciently:
declarations
NT = 3Months = {'Jan', 'Feb', 'Mar'}MAXP = 8.4
Filename= "mydata.dat"
end-declarations

If such constants may change with the model instance that is solved, their deﬁnition should be moved
into the parameters block (notice that this possibility only applies to simple types, excluding sets orarrays):parameters
NT = 3
MAXP = 8.4
Filename = "mydata.dat"
end-parameters

Mosel interprets these parameters as constants, but their value may be changed at every execution of amodel, e.g.
mosel exec mymodel NT=5 MAXP=7.5 Filename="mynewdata.dat"

B.2

Naming sets
It is customary in mathematical models to write index sets as 1, ..., N or the like. Instead of translating

Fair Isaac Corporation Conﬁdential and Proprietary Information

228

Good modeling practice with Mosel

this directly into Mosel code like the following:
declarations
x: array(1..N) of mpvar
end-declarations
sum(i in 1..N) x(i) >= 10

it is recommended to name index sets:
declarations
RI = 1..N
x: array(RI) of mpvar
end-declarations
sum(i in RI) x(i) >= 10

The same remark holds if several loops or operators use the same intermediate set(s). Instead offorall(i in RI | isodd(i)) x(i) is_integer
forall(i in RI | isodd(i)) x(i) <= 5sum(i in RI | isodd(i)) x(i) >= 10

which calculates the same intermediate set of odd numbers three times, it is more efﬁcient to deﬁne
this set explicitly and calculate it only once:
ODD:= union(i in RI | isodd(i)) {i}forall(i in ODD) x(i) is_integer
forall(i in ODD) x(i) <= 5sum(i in ODD) x(i) >= 10

Alternatively, loops of the same type and with the same index set(s) may be regrouped to reduce thenumber of times that the sets are calculated:
forall(i in RI | isodd(i)) dox(i) is_integer
x(i) <= 5
end-dosum(i in RI | isodd(i)) x(i) >= 10

B.3

Finalizing sets and dynamic arrays
The declaration of an array in Mosel has one of these two forms
1. Explicit declaration as sparse array by using one of the keywords dynamic or hashmap.
2. ‘Standard’ declaration, resulting in a dense array that is either static (all index sets are known) or
not ﬁxed (some or all indexing sets are unknown at the point where the declaration takes place).
If an array is used to represent dense data one should avoid deﬁning it as a sparse array as that uses
more memory and is slower than the corresponding dense array.
In many optimization models, dense arrays are created as non-ﬁxed arrays because their contents is
initially unknown—but there is no real need to treat them as dynamic structures throughout the wholemodel as they remain unchanged once they have been initialized.
The automatic ﬁnalization mechanism of Mosel therefore transforms such initially dynamic
sets/non-ﬁxed arrays as to handle them more efﬁciently. As an additional advantage, set ﬁnalization

Fair Isaac Corporation Conﬁdential and Proprietary Information

229

Good modeling practice with Mosel

allows Mosel to check for ‘out of range’ errors that cannot be detected if the sets are allowed to growdynamically.


By default, initializations blocks ﬁnalize the sets they initialize and also the index sets ofinitialized dense arrays.
Data of non-dynamic arrays is read before ﬁnalization of the index sets in order to create thearrays static.
Arrays that are not explicitly declared as sparse arrays are only allocated when they are ﬁrst
accessed: this allows these arrays to be static even if their index sets are ﬁnalized after thedeclaration of the arrays.

So, code like the following example
declarations
S: set of string
A,B: array(S) of real
x: array(S) of mpvar
end-declarations
initializations from "mydata.dat"A
end-initializationssum(s in S) B(s)⁎x(s)where all arrays are declared as dense arrays that are not ﬁxed (their size is not known at theirdeclaration) but only A that is initialized using a data ﬁle really needs to be non-ﬁxed, will be treated by
Mosel as if you had written the following
declarations
S: set of string
A: array(S) of real
end-declarations
initializations from "mydata.dat"A
end-initializations
finalize(S)declarations
B: array(S) of real
x: array(S) of mpvar
end-declarations

That is, B and x are created as static arrays, making the access to the array entries more efﬁcient.
As a general rule, the following sequence of actions gives better results (in terms of memory
consumption and efﬁciency):
1. Declare data arrays and sets that are to be initialized from external sources.
2. Perform initializations of data.
3. Finalize all related sets.
4. Declare any other arrays indexed by these sets (including decision variable arrays).
Note: there are several possibilities to stop Mosel from applying automatic ﬁnalization to model
objects:

Fair Isaac Corporation Conﬁdential and Proprietary Information

230

Good modeling practice with Mosel



Declare arrays explicitly as dynamic or hashmap arrays. (See examples in Sections 3.2 and3.3.1.)



Declare sets explicitly as dynamic in which case they cannot be ﬁnalized.



Use control parameter autofinal to enable/disable automatic ﬁnalization locally:
setparam("autofinal", false)
initializations from "datafile.dat"...
end-initializations
setparam("autofinal", true)Use option noautofinal to disable automatic ﬁnalization globally for the whole model:
model "modelname"options noautofinal

B.4

Ordering indices
Especially when working with sparse arrays, the sequence of their indices in loops should correspondas far as possible to the sequence given in their declaration. For example an array of variables declaredby:declarations
A,B,C: range
x: array(A,B,C) of mpvar
end-initializations

that is mostly used in expressions like sum(b in B, c in C, a in A) x(a,b,c) should
preferrably be declared as
declarations
A,B,C: range
x: array(B,C,A) of mpvar
end-declarations

or alternatively the indices of the loops adapted to the order of indices of the variables.

B.5

Use of exists
The Mosel compiler is able to identify sparse loops and optimizes them automatically, such as in thefollowing example:
declarations
I=1..1000
J=1..500
A: dynamic array(I,J) of real
x: array(I,J) of mpvar
end-declarations
initializations from "mydata.dat"A
end-initializations
C:= sum(i in I,j in J | exists(A(i,j))) A(i,j)⁎x(i,j) = 0

Fair Isaac Corporation Conﬁdential and Proprietary Information

231

Good modeling practice with Mosel

Notice that we obtain the same deﬁnition for the constraint C with the following variant of the code, but
no loop optimization takes place:
C:= sum(i in I,j in J) A(i,j)⁎x(i,j) = 0

Here all index tuples are enumerated and the corresponding entries of A are set to 0. Similarly, if not all
entries of x are deﬁned, the missing entries are interpreted as 0 by the sum operator.
The following rules have to be observed for efﬁcient use of the function exists, :1. The arrays have to be indexed by named sets (here I and J):
A: dynamic array(I,J) of real
H: hashmap array(I,J) of real
B: dynamic array(1..1000,1..500) of real

! can be optimized! can be optimized! cannot be optimized

2. The same sets have to be used in the loops:
forall(i in I,j in J | exists(A(i,j)))
K:=I; forall(i in K,j in 1..500 | exists(A(i,j)))! fast! slow

3. The order of the sets has to be respected, particularly for dynamic arrays:
forall(i in I,j in J | exists(A(i,j)))forall(j in J,i in I | exists(H(i,j)))forall(j in J,i in I | exists(A(i,j)))! fast! slower! slowest

4. The exists function calls have to be at the beginning of the condition:
forall(i in I,j in I | exists(A(i,j)) and i+j<>10)
forall(i in J,j in J | i+j<>10 and exists(A(i,j)))! fast! slow

5. The optimization does not apply to or conditions:forall(i in I,j in J | exists(A(i,j)) and i+j<>10)
forall(i in I,j in J | exists(A(i,j)) or i+j<>10)

B.6! fast! slow

Structuring a model
Procedures and functions may be introduced to structure a model. For easy readability, the length of a
subroutine should not exceed the length of one page (screen).
Large model ﬁles could even be split into several ﬁles (and combined using the include statement).

B.7

Transforming subroutines into user modules
The deﬁnitions of subroutines that are expensive in terms of execution time and are called very often(e.g. at every node of the Branch-and-Bound search) may be moved to a user module. Via the Mosel
Native Interface it is possible to access and change all information in a Mosel model during its
execution. See the Mosel Native Interface User Guide for a detailed description of how to deﬁne user
modules.

B.8

Algorithm choice and parameter settings
The performance of the underlying solution algorithm has, strictly speaking, nothing to do with the
efﬁciency of Mosel. But for completeness’ sake the reader may be reminded that the subroutines

Fair Isaac Corporation Conﬁdential and Proprietary Information

232

Good modeling practice with Mosel

getparam and setparam can be used to access and modify the current settings of parameters ofMosel and also those provided by modules, such as solvers.
The list of parameters deﬁned by a module can be obtained with the Mosel command
exam -p module_nameWith Xpress Optimizer (module mmxprs) you may try re-setting the following control parameters for thealgorithm choice:

LP: XPRS_PRESOLVE
MIP: XPRS_PREPROBING, XPRS_MIPPRESOLVE, XPRS_CUTSTRATEGY, XPRS_HEURSTRATEGY,XPRS_SBEFFORT, XPRS_NODESELECTION
Other useful parameters are the criteria for stopping the MIP search: XPRS_MAXNODE,XPRS_MAXMIPSOL, XPRS_MAXTIME, the cutoff value (XPRS_MIPADDCUTOFF,XPRS_MIPABSCUTOFF), and various tolerance settings (e.g. XPRS_MIPTOL).

Refer to the Xpress Optimizer Reference Manual for more detail.
You may also add priorities or preferred branching directions with the procedure setmipdir(documented in the chapter on mmxprs in the Mosel Reference Manual).

Fair Isaac Corporation Conﬁdential and Proprietary Information

233

APPENDIX C

Character encoding in Mosel
This chapter addresses a number of questions relating to character encoding, in particular:

C.1

What is a "character encoding", "character map", "code page"?

What is Unicode?

What is the meaning of UTF-8,16,32 and UCS-2?



What is a BOM?

Which character encoding is conﬁgured on my computer?

Which ﬁles are concerned by character encoding in Mosel?

How can I convert the character encoding of a text ﬁle?

What is a "character encoding", "character map", "code page"?
Although these terms are not strictly equivalent they all relate to the same problematic: how torepresent a symbol (or character) in a computer system. Such a representation is characterized by 2
properties:
1. a character map to associate each symbol to a unique numerical ID (or code point). For instance
US-ASCII deﬁnes 128 positions to represent the letters, digits and punctuation commonly used in
English: "exclamation mark" (!) has code point 33, "zero" has code point 48, "Capital Letter A" hascode point 65, etc.
2. an encoding method to actually represent each code point in memory. With ASCII, 7 bits are
sufﬁcient to encode the entire code set: each character is usually encoded on a single byte.
Various character encodings have been invented to satisfy local requirements around the world. For
example, ISO-8859-1 is an 8-bit extension of ASCII (i.e. the 128 ﬁrst code points of this encoding are thesame as ASCII) speciﬁcally designed for a group of European languages: it adds a set of accented
letters to standard ASCII. Another version, ISO-8859-7 is suitable for Greek but cannot represent
accented letters such as those used in French.
When the number of code points exceeds 256 it is required to switch to a multi-byte encoding. Shift-JIS(used in Japan) is an example of multi-byte encoding: each character is encoded using either 1 or 2
bytes.
Typically a computer system is set up with some national encoding suitable to handle the symbols
required by the local language. For instance a Windows system installed in Germany uses encoding
CP1252 (where CP stands for Code page) that supports symbols like ’ß’ or ’ö’ but will not be able todisplay any Greek (e.g. ’ θ ’) or Hebrew characters (e.g. ’ ℵ ’).

Fair Isaac Corporation Conﬁdential and Proprietary Information

234

Character encoding in Mosel

C.2

What is Unicode?
Unicode is a universal encoding aimed at representing all known symbols such that a single encoding
can be used for any country/language. Unicode is widely adopted and most computer systems use it
internally to store character strings: the Windows operating system (and ﬁle system) uses thisencoding as well as most Unix/Linux systems. Programming environments like Java or .NET are also
based on Unicode.
Note that in China the GB18030 encoding is preferred to Unicode: this is a universal encoding publishedby the Chinese National Standard.

C.3

What is the meaning of UTF-8,16,32 and UCS-2?
Unicode deﬁnes the mapping between code points and symbols, the effective encoding is speciﬁed bya Unicode Transformation Format (UTF). The most commonly used UTF encodings are:UTF-32a character is represented by a 4-byte integer

UTF-16a character is represented by 1 or 2 2-byte integers

UTF-8

a character requires between 1 and 4 bytes

Compared to the other UTF encodings UTF-8 has the advantage of being compatible with ASCII: a textthat consists only of ASCII characters has the same representation in UTF-8 and ASCII. As aconsequence UTF-8 is also more compact than the other UTF encodings for English and most
European languages (because the majority of symbols are included in the ASCII set).
UCS-2 (Universal Character Set v2) is a deprecated encoding originally used in Windows and Java: it
encodes each character on a 2-bytes integer and is therefore limited to the ﬁrst 65536 code points ofUnicode, this is why it has gradually been replaced by plain UTF-16.

C.4

What is a BOM?
For UTF-16 and UTF-32 the byte ordering has to be known (in fact we should refer to UTF-16LE,
UTF-16BE, UTF-32LE, UTF-32BE to take into account the endianness of the encoding). In order to avoid
incorrect interpretation of these encodings a Byte Order Mark (BOM) may be put at the beginning of
documents: it consists in a sequence of bytes that identiﬁes both the encoding (UTF-16 or UTF-32) andthe byte ordering used (Little Endian or Big Endian).
Although UTF-8 does not require any byte ordering information, a dedicated BOM can be used with thisencoding: its primary purpose is to differentiate UTF-8 from other byte-oriented encodings. If notinterpreted this marker takes the form of the 3-characters sequence "ï¿" (in ISO-8859-1 or CP1252): a
document starting with this sequence must be read with an UTF-8 enabled software.

C.5

Which character encoding is conﬁgured on my computer?
You can use the command ’xprnls info’ of the XPRNLS command tool to identify which encoding isused on your system. The following example shows the output produced for western European
Windows / ’latin’ encoding with UK English as the selected language (the program output is highlighted
in bold face):

Fair Isaac Corporation Conﬁdential and Proprietary Information

235

Character encoding in Mosel>xprnls info
Language: en
Default encodings:
System:
CP1252
Console:CP437
File names: CP1252
Wide chars: UTF-16LE

Note that Xpress Workbench works with UTF-8 character encoding, independent of the system settings.

C.6

Which ﬁles are concerned by character encoding in Mosel?
Starting with version 4.0 Mosel is working in UTF-8. This concerns

the internal representation of text

all external APIs (i.e. all Mosel libraries)the communication with the system via Unicode (Windows) or system encoding (Posix)

All streams and text ﬁles default to UTF-8. There is no impact on applications that only use pure ASCII(ﬁrst 127 characters), but text data ﬁles and source code using other encodings might requireconversions or tagging. Note that no changes are required for other ﬁle types such as spreadsheets or
databases.
Model source and text data ﬁles in Mosel format: Specify the encoding with the annotation!@encoding. For example if you are editing your model with an editor that employs the encoding
CP1252:!@encoding CP1252
model "my testmodel"...

Other text/string input or output: Convert the encoding via the enc: preﬁx to ﬁle names and streams
or by using the conversion routines of the XPRNLS library or command tool (see paragraph ’How can I
convert the character encoding of a text ﬁle’ below).

C.7

How can I convert the character encoding of a text ﬁle?
Text format data ﬁles (other than the Mosel initializations format for which the !@encoding
marker can be used) such as CSV ﬁles or ﬁles accessed via fopen that do not use UTF-8 encoding
need to be converted with the ’enc:’ preﬁx when accessing them from within a Mosel model.
Example:! Encoding names are operating system dependent, eg CP1252, ISO88591
fopen(enc:GB18030,testdata.txt", F_INPUT)

It is usually preferrable to specify the encoding used by a data ﬁle as shown above, but Mosel also
implements shorthands for encodings conﬁgured on the system running the model.
! Encoding aliases:! raw, sys, wchar, fname, tty, ttyin, stdin, stdout, stderr
initializations to "enc:sys,mmsheet.csv:testoutput.csv"...
end-initializations

Fair Isaac Corporation Conﬁdential and Proprietary Information

236

Character encoding in Mosel

Using the preﬁx enc:sys means that the default system encoding is employed (which corresponds tothe behaviour of Mosel versions prior to Mosel 4).
On the API level, you can use the XPRNLS library to convert to/from UTF-8 encoding (please see thereference manual XPRNLS command tool and library for the full documentation of its functionality):

this library is platform independent and has no external dependency

it handles encoding conversions between UTF-8 and local encodings

it implements UTF-8/16/32(LE+BE), ISO-8859-1/15, ASCII, CP1252



other supported encodings depend on the operating system// Open a file using the C function 'fopen' with a file name coming from Mosel
f = fopen(XNLSconvstrto(XNLS_ENC_SYS,filename,-1,NULL),"r");Alternatively, you can use the XPRNLS command tool for converting the character encoding of text ﬁlesbetween any two supported encodings:
xprnls conv -f CP1252 -t UTF8 -o outfile.txt myfile.txt

Note: you can display the list of the available xprnls commands by entering
xprnlsat the command prompt.

Fair Isaac Corporation Conﬁdential and Proprietary Information

237

APPENDIX D

Contacting FICO
FICO provides clients with support and services for all our products. Refer to the following sections formore information.

Product support
FICO offers technical support and services ranging from self-help tools to direct assistance with a FICO
technical support engineer. Support is available to all clients who have purchased a FICO product andhave an active support or maintenance contract. You can ﬁnd support contact information and a link to
the Customer Self Service Portal (online support) on the Product Support home page(www.ﬁco.com/en/product-support).
The FICO Customer Self Service Portal is a secure web portal that is available 24 hours a day, 7 days aweek from the Product Support home page. The portal allows you to open, review, update, and close
cases, as well as ﬁnd solutions to common problems in the FICO Knowledge Base.
Please include ’Xpress’ in the subject line of your support queries.

Product education
FICO Product Education is the principal provider of product training for our clients and partners.
Product Education offers instructor-led classroom courses, web-based training, seminars, and training
tools for both new user enablement and ongoing performance support. For additional information, visit
the Product Education homepage at www.ﬁco.com/en/product-training or email
producteducation@ﬁco.com.

Product documentation
FICO continually looks for new ways to improve and enhance the value of the products and services weprovide. If you have comments or suggestions regarding how we can improve this documentation, let
us know by sending your suggestions to techpubs@ﬁco.com.
Please include your contact information (name, company, email address, and optionally, your phone
number) so we may reach you if we have questions.

Fair Isaac Corporation Conﬁdential and Proprietary Information

238

Contacting FICO

Sales and maintenanceIf you need information on other Xpress Optimization products, or you need to discuss maintenancecontracts or other sales-related items, contact FICO by:

Phone: +1 (408) 535-1500 or +44 207 940 8718



Web: www.ﬁco.com/optimization and use the available contact forms

Related services
Strategy Consulting: Included in your contract with FICO may be a speciﬁed amount of consulting time
to assist you in using FICO Optimization Modeler to meet your business needs. Additional consulting
time can be arranged by contract.
Conferences and Seminars: FICO offers conferences and seminars on our products and services. For
announcements concerning these events, go to www.ﬁco.com or contact your FICO accountrepresentative.

FICO Community
The FICO Community is a great resource to ﬁnd the experts and information you need to collaborate,
support your business, and solve common business challenges. You can get informal technicalsupport, build relationships with local and remote professionals, and improve your business practices.
For additional information, visit the FICO Community (community.ﬁco.com/welcome).

About FICO
FICO (NYSE:FICO) powers decisions that help people and businesses around the world prosper.
Founded in 1956 and based in Silicon Valley, the company is a pioneer in the use of predictive analytics
and data science to improve operational decisions. FICO holds more than 165 US and foreign patents
on technologies that increase proﬁtability, customer satisfaction, and growth for businesses inﬁnancial services, telecommunications, health care, retail, and many other industries. Using FICO
solutions, businesses in more than 100 countries do everything from protecting 2.6 billion payment
cards from fraud, to helping people get credit, to ensuring that millions of airplanes and rental cars arein the right place at the right time. Learn more at www.ﬁco.com.

Fair Isaac Corporation Conﬁdential and Proprietary Information

239

IndexSymbols⁎, 7, 59+, 15, 59, 62+=, 60, 62,, 15-, 15, 59, 62-=, 60, 62::, 13, 221
<=, 7
=, 7
>=, 7
A
abs, 74
addcuts, 87
and, 40
annotation, 200
character encoding, 236
annotation category, 200
annotation name, 200
application
access model, 105compile model, 103
data exchange, 108, 124, 135
execute model, 104
model parameters, 105
solution access, 105
array, 12, 53
automatic, 56
declaration, 13
dense, 25, 107, 110, 123, 124, 134, 135, 156, 229
dynamic, 25, 27, 84, 91, 155
ﬁnalize, 229
index set, 55
initialization, 13, 17
initialization operator, 13
input data format, 25
multi-dimensional, 13, 25
non-ﬁxed, 25
sparse, 107, 111, 123, 125, 134, 136, 155static, 27, 155, 229
array, 40, 53, 56, 80
as, 40as counter, 49
automatic array, 56
automatic ﬁnalization, 28, 58, 229
disable, 231
Bbaseline scenario, 184
BIM ﬁle, 103bin, 168Fair Isaac Corporation Conﬁdential and Proprietary Information

binary format, 168
binary model ﬁle, 175
binary variable, 32
blending constraint, 16
BOM, see Byte Order Markboolean, 40, 53
bounded variable, 16
BRE, 198
break, 40, 52
Byte Order Mark, 235
CC interface, 103
callback, 88
case, 40cb, 112, 168
ceil, 90
character encoding
compatibility, 236
conversion, 236
character map, 234
code page, 234
column, see variablecolumn generation, 89
combining solvers, 187
comment, 7
multiple lines, 7
comparison
list, 62set, 60
comparison tolerance, 87
compile, 8, 103
to memory, 175
compile, 175
condition, 26, 46, 232conditional generation, 26
conditional loop, 49
connect, 177constant, 12
constant, 40, 194
constant list, 61
constant set, 57
constant type, 55
constraint
hide, 100
MVLB, 85
named, 14
non-negativity, 6, 7
type, 99
Constraint Programming, 187
continuation line, 15
count, 40, 50

240Indexcounter, 40
counters, 49
CP, see Constraint Programming
create, 26, 55
cross-recursion, 73
csv, 173
CSV format, 22
cut generation, 83
cut manager, 87
cut manager entry callback, 88
cut pool, 87
cutting plane method, 83
cutting stock problem, 89D
datacommunication, 108, 124, 135
declaration, 156
dense, 156
exchange with application, 108, 124, 135
initialization, 156
input from database, 18
input from ﬁle, 17, 25, 28
multi-dimensional array, 25, 28
output, 78
sparse, 156
sparse format, 28
data ﬁle, 167
format, 17, 25
data format
dense, 110, 124, 135
sparse, 111, 125, 136
database, 18date, 39
date, 192, 194
datefmt, 193
datetime, 192, 195
debug, 149
debugger, 149
debugging, 41
decision variable, see variable, 5array, 13
declarationarray, 13
public, 161
subroutine, 73
declarations, 7, 40, 71
decomposition, 174
default I/O driver, 167
deflate, 174delcell, 54
delete temporary ﬁlesmodel, 121, 131
dense, 107, 123, 134
dense array, 25, 54
dense data, 55, 156, 229
dense data format, 110
dense format, 124, 135
deviation variable, 99difference, 59, 60

Fair Isaac Corporation Conﬁdential and Proprietary Information

Dim, 135
diskdata, 29, 79, 80, 171
distributed computing, 177div, 40do, 40doc, 201
dotnet, 138, 141, 169
dotnetraw, 135, 136, 170
dynamic, 25, 40, 54, 155, 229
dynamic array, 25, 27, 54, 84, 155
dynamic data input, 114, 127, 138
dynamic list, 61
dynamic output retrieval, 113, 127, 138
dynamic set, 57
E
efﬁciency, 152elif, 40
else, 40
embeddingdata exchange, 108, 124, 135
model access, 105
enc:, 236
encoding, 236
encoding method, 234
end, 40
end-declarations, 7
end-do, 48
end-function, 70
end-initializations, 17
end-model, 7
end-procedure, 70
enumeration
dense array, 107, 123, 134
inverse order, 62set, 106, 122, 133
sparse array, 108, 124, 135
ERE, 198
error
data, 41
logical, 41
redirection, 117, 130, 141, 145
run time, 42
syntax, 41
error handling, 30
error stream, 145
redirecting, 31
ETC_SPARSE, 79, 80
ETC_OUT, 78
ETC_SPARSE, 78
evaluation, 40, 80
exam, 39, 168
excel, 173
Excel spreadsheet, 21
execute, 8
execution speed, 152
exists, 26, 156, 232
exportprob, 27, 167
extended ﬁle name, 167

241

IndexF
F_APPEND, 78
F_OUTPUT, 78
false, 40
fclose, 31, 78, 167
fdelete, 176
feasibility tolerance, 87
ﬁeld, 53
access, 66
ﬁlegeneralized, 167
ﬁle handling, 167
ﬁle output, 78
solution, 80
finalize, 28
ﬁnalized, 27
FindIdentifier, 133
findIdentifier, 122
ﬁnish, 104, 108
FirstIndex, 133
ﬁxed set, 57
ﬁxed size array, 54
ﬂow control, 46
fopen, 31, 78, 167
forall, 14, 26, 40, 48, 49, 51
forall-do, 48
formatdate, 193
real number output, 81
text output, 76
time, 193
forward, 40, 73, 88
free variable, 96
from, 40
fully qualiﬁed entity name, 163
function, 70, 232
function, 40, 70
Ggeneralized ﬁle, 167
getFirstTEIndex, 124
getannotations, 201
getasnumber, 194, 195
getcoeff, 74
getDimension, 124
getexitcode, 175
getFirstIndex, 122, 123
getfstat, 30
getLastIndex, 122
getobjval, 8
getparam, 30, 163
getreadcnt, 30
getreverse, 62
getsize, 60
getsol, 8, 36, 74
gettype, 99
getvalue, 175
global annotation, 200
gnuplot, 173
Goal Programming, 98

Fair Isaac Corporation Conﬁdential and Proprietary Information

Archimedian, 98
lexicographic, 98
pre-emptive, 98graphics, 178gzip, 174H
hashmap, 40, 54, 155, 229
hashmap array, 54
head, 62hide
constraint, 100
hybrid solution approaches, 187
I
I/O driver, 80, 167bin, 168, 169
cb, 168
csv, 173
default, 167
deﬂate, 174
diskdata, 171
dotnet, 169
dotnetraw, 170
excel, 173
gzip, 174
java, 171
jraw, 172
mem, 168
mempipe, 172
null, 169oci, 172
odbc, 173
pipe, 173
raw, 169
rcmd, 172rmt, 172
shmem, 172
sysfd, 169
text, 173url, 171
xls, 173
xlsx, 173
xsrv, 172xssh, 172
I/O error, 30
if, 40, 49
if-then, 46
if-then-else, 50
implicitly dynamic array, 229
imports, 40, 157
in, 40, 60
include, 40, 157, 232
index
multiple, 49
index set, 12, 15
index set type, 55
Indices, 134
info, 155
initialisations, 40242

Indexinitializationarray, 13, 17
list, 61set, 57
initializations, 17, 28, 40, 78, 125, 136, 167, 176,
230
initializations from, 17
initializations to, 164
Insight, see Xpress Insightinteger, 40, 53
integer knapsack problem, 92
Integer Programming, 36
integer variable, 32
inter, 40interrupt
loop, 52
intersection, 59
ioctrl, 30
iostatus, 30
IP, see Integer Programming
is_binary, 40
is_continuous, 40
is_free, 40
is_integer, 40
is_partint, 40
is_semcont, 40
is_semint, 40
is_sos1, 33, 36, 40
is_sos2, 33, 40
is_binary, 32
is_integer, 32
is_partint, 32
is_semcont, 33
is_semint, 33
isqueueempty, 177
isvalid, 193
Jjava, 126, 130, 171
jraw, 124, 125, 172K
knapsack problem, 11
integer, 92
Llargest common divisor, 50
LastIndex, 133
limit, see boundlinctr, 40, 53
line break, 15
Linear Programming, 4, 36
Linear Programming problem, 6
list, 53
comparison, 62
concatenation, 62
constant, 61
dynamic, 61
enumeration, 62
initialization, 61

Fair Isaac Corporation Conﬁdential and Proprietary Information

merging, 63
operators, 62
list, 40, 53
load, 8, 103load, 175
loadprob, 118
loop, 14, 46, 48
conditional, 49, 156
interrupting, 52
nested, 52
sparse, 231
LP, see Linear Programming
lsmods, 155lssymb, 155M
match
regular expression, 198
Mathematical Programming, 4max, 40, 49
maximize, 118
maximum, 47
mc, 201
mem, 110, 168
memory consumption, 152
mempipe, 172
meta data, 200
min, 40
minimum, 47
MIP, see Mixed Integer Programming
MIQP, see Mixed Integer Quadratic Programming
Mixed Integer Programming, 4, 32, 83
Mixed Integer Quadratic Programming, 187
mmdotnet, 169
mmetc, 29, 79, 171
mmhttp, 171
mminsight, 178
mmjava, 171
mmjobs, 172, 174
mmoci, 172
mmodbc, 19, 173
mmsheet, 21
mmsheet, 19, 173
mmsvg, 178
mmsystem, 39, 173
mmxml, 180
mmxml, 179
mmxprs, 8, 39, 42, 118
mod, 40model, 6
access from application, 105compile, 103
coordination, 175
data from application, 108, 124, 135
execute, 8, 104
parameters, 105
reset, 104, 108, 120, 131
run, 8
unload, 104
model, 7, 40, 157

243

Indexmodel documentation, 203model ﬁle, 103
model structure, 232
modeling
efﬁciency, 152
module, 39, 167
I/O driver, 169
monthnames, 193
Mosel Remote Launcher, 172
MOSEL_DSO, 158
moseldoc, 203
MP, see Mathematical Programming
mpproblem, 93
mpvar, 7, 13, 25, 40, 53
multiple indices, 49
multiple models, 174
multiple nodes, 177
multiple problems, 92, 93
MVLB constraint, 85N
nameconstraint, 14
namespace, 40, 163
namespace group, 163
nbread, 28, 30
negation, 60
nested loops, 52
next, 40
nextTEIndex, 124
nextIndex, 123
NLP, see Non-linear Programming
noautofinal, 231
noindex, 110
Non-linear Programming, 187
non-negative variable, 6, 7
non-negativity constraint, 6, 7
not, 40, 60
nsgroup, 40, 163
nssearch, 40, 163
null, 31, 169
number output format, 81Oobjective function, 6, 7
oci, 172
ODBC, 19
odbc, 173of, 40open, 169
operatorcounter, 50
set, 60
optimization, 8
options, 40
or, 40, 232
output, 8
disable, 169ﬁle, 78
formatted, 98

Fair Isaac Corporation Conﬁdential and Proprietary Information

formatting, 76
redirection, 117, 130, 141, 145, 169
splitting, 169
output ﬁle, 145
overloading, 74
Ppackage, 157
internal name, 158
location, 158
name, 158
package, 40, 157, 158
package parameter, 161parallel solving, 174
parameter, 18
comparison tolerance, 87
global, 71
local, 71
number output format, 81
subroutine, 71
parameters, 105, 110, 124, 135
parameters, 18, 40, 228parser, 41
partial integer variable, 32
perfect number, 48
pipe, 173
prime number, 59, 105, 121, 132
problem
decomposition, 174
multiple, 92, 93
solving, 8
procedure, 70, 232
procedure, 40, 70
prod, 40
profile, 152
proﬁler, 152project planning problem, 34
public, 40, 65, 89, 105, 121, 161, 184
Q
QCQP, see Quadratically Constrained Quadratic
Programming
QP, see Quadratic Programming
qsort, 73, 195
Quadratic Programming, 4, 187
Quadratically Constrained Quadratic Programming,
187
quick sort, 73
quit, 149
R
range, 40, 48
range set, 12
raw, 111, 169
rcmd, 172
read, 28
readcnt, 30
readln, 28
real
output format, 81244

Indexreal, 40, 53
REALFMT, 81
record, 53
record, 40, 53, 65
recursion, 72, 95
redirecting output, 169
reference row entries, 33
REG_EXTENDED, 198
REG_ICASE, 198
regex, see regular expression
regular expression, 198
matching, 198
relocating documentation, 206
remote execution, 177
repeat, 40
repeat-until, 48, 51, 52
requirements, 40
resetmodel, 104, 108, 120, 131
reset, 54
return, 40
returned, 70
reverse, 62rmt, 172row, see constraint
run, 8, 103
run, 175
S
scalarI/O in memory, 115, 129, 140
selection statements, 46
semi-continuous integer variable, 33
semi-continuous variable, 33
set, 53, 105, 121, 132
comparison, 60
constant, 27, 57
dynamic, 57
ﬁnalize, 229
ﬁnalized, 27
ﬁxed, 57, 58
initialization, 57
maximum, 47
minimum, 47
string indices, 15
type, 53
set, 40, 53
set of constants, 194
set of strings, 15
set operation, 59
set operator, 60
sethidden, 92, 100
setparam, 30, 163
shared, 40
shell sort, 51
shmem, 172
sleep, 151
SLP, 187
solution output, 80
solution value, 106, 122, 133

Fair Isaac Corporation Conﬁdential and Proprietary Information

solverscombining, 187
solving, 8sortingdate, 195
time, 195
sorting algorithm, 39, 51, 73
sparse, 25, 28, 79
loop, 231
sparse array, 54, 155
sparse data, 55, 155
sparse format, 107, 111, 123, 125, 134, 136
sparsity, 23
Special Ordered Set of type one, 33, 36
Special Ordered Set of type two, 33
spreadsheet, 18
static array, 155stop, 176
strfmt, 76
string, 40, 53
submodel, 174
coordination, 175
interaction, 175
status, 175, 209
subproblem, 93, 100
subroutine, 70, 232
declaration, 73
deﬁnition, 73
overloading, 74
parameter, 71
subscript, 12
subset, 60
Successive Linear Programming, 95
sum, 40, 49, 62
summation, 14
superset, 60
syntaxregular expression, 198
syntax error, 41
sysfd, 169
system call, 39
Ttable, see arraytail, 62tee, 169
TEIndices, 135
temporary directory, 169
temporary ﬁlesdelete, 121, 131
termination, 104, 108
text, 31, 173, 197
textarea, 198
then, 40time, 192, 195
time measurement, 39
timefmt, 193
tmp, 169
to, 40tolerance

245Indexcomparison, 87
feasibility, 87
real number output, 81
transport problem, 23, 107, 123, 134
true, 40
type
array, 53
basic, 53constant, 12
constraint, 99
elementary, 53
external, 53
list, 53
MP, 53
record, 53
set, 53
structured, 53
user, 68U
unbounded variable, 96
Unicode, 235
Unicode Transformation Format, 235
union, 59
union, 40, 49
unloadmodel, 104
until, 40url, 171
uses, 19, 40, 157
UTF, see Unicode Transformation Format
Vvariable, 5binary, 14, 32
bounds, 16
conditional creation, 26
free, 96
integer, 14, 32
lower bound, 7
non-negative, 6, 7
partial integer, 32
semi-continuous, 33
semi-continuous integer, 33
unbounded, 96
VDL, see View Deﬁnition Language
version, 40
View Deﬁnition Language, 186Xxls, 173
xlsx, 173
XML document, 180
XML path, 180
xmldoc, 180
XPRDstoprunmod, 211
Xpress Insight, see Xpress Insight, 178
Xpress Kalis, 187
Xpress Optimizer, 118
Xpress Workbench, see Workbench
XPRM, 120, 131
XPRM.Init(), 131
XPRM_F_ERROR, 145
XPRM_F_OUTPUT, 145
XPRMcompmod, 167
XPRMexecmod, 104
XPRMexecmod, 105
XPRMfinddso, 118
XPRMfindident, 106
XPRMfinish, 104, 105
XPRMgetfirstarrtruentry, 108
XPRMgetnextarrtruentry, 108
XPRMgetarrdim, 108
XPRMgetarrsets, 108
XPRMgetelsetval, 106
XPRMgetfirstarrentry, 107
XPRMgetfirstsetndx, 106
XPRMgetlastsetndx, 106
XPRMgetnextarrentry, 107
XPRMloadmod, 104, 167
XPRMresetmod, 104, 108
XPRMrunmod, 104, 167
XPRMsetdefstream, 117
xprmsrv, 172
XPRMunloadmod, 104, 105
xprnls, 235, 237
XPRNLS command tool, 235, 237
XPRNLS library, 237
XPRS_PROBLEM, 118
XPRS_LOADNAMES, 43
XPRS_VERBOSE, 43
xsrv, 172xssh, 172Z
ZEROTOL, 87
zlib, 174W
wait, 175
warning, 42
while, 40, 48, 50–52, 60
while-do, 48, 50
with, 40, 94
Workbench, 9
workdir, 30
write, 8, 76, 79
writeln, 8, 28, 76, 78, 79

Fair Isaac Corporation Conﬁdential and Proprietary Information

246