Decision Optimization with IBM ILOG CPLEX Optimization Studio: A Hands-On Introduction to Modeling with the Optimization Programming Language (OPL) (Graduate Texts in Operations Research) 3662654806, 9783662654804

This textbook offers a comprehensive, up-to-date introduction to the Optimization Programming Language (OPL). Embedded i

110 26 7MB

English Pages 294 [279] Year 2022

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Decision Optimization with IBM ILOG CPLEX Optimization Studio: A Hands-On Introduction to Modeling with the Optimization Programming Language (OPL) (Graduate Texts in Operations Research)
 3662654806, 9783662654804

Table of contents :
Preface
Table of contents
List of abbreviations
Part 1 Lessons
1 Introduction
1.1 Modeling and solving planning and decision-making problems
1.2 IBM ILOG CPLEX Optimization Studio
1.3 Introductory example
1.4 Content covered in the book
2 IBM ILOG CPLEX Optimization Studio—A primer
2.1 Licenses and installation
2.2 Starting the Studio
2.3 First steps
2.3.1 Importing a sample project
2.3.2 The components of a project
2.3.3 Solving a model instance
2.3.4 Studio components
2.3.5 Analysis of models, results, and the solution process
2.3.6 Creating a new project and importing existing projects
2.4 Studio Help and other resources
3 Setting up a model
3.1 Declaration and initialization of model parameters
3.1.1 Declaration
3.1.2 Initialization
3.2 Declaration of decision variables
3.3 Objective function
3.4 Constraints
3.5 Solution of Case Study 1
3.6 Exercises
4 Data structures and related OPL language elements
4.1 Ranges
4.2 Arrays
4.2.1 Declaration and explicit initialization of arrays and array access
4.2.2 Arrays of decision variables
4.2.3 Multidimensional arrays
4.2.4 Further possibilities for initializing arrays
4.2.5 Placeholders and filters
4.3 Sets
4.3.1 Declaration and explicit initialization of sets
4.3.2 Further options for initializing sets
4.3.3 Index sets
4.3.4 Ordered sets
4.3.5 Set operations and functions for sets
4.4 Aggregate operators
4.4.1 Summation
4.4.2 Other aggregate operators
4.5 The forall-quantifier
4.6 Solution of Case Study 2
4.7 Exercises
5 Introduction to IBM ILOG Script
5.1 Structure and basic language elements of IBM ILOG Script
5.2 OPL data elements and IBM ILOG script variables
5.3 Other language elements
5.3.1 Control structures
5.3.2 Mathematical operators and functions
5.3.3 Properties and functions of ranges, arrays, and sets
5.3.4 String processing
5.3.5 Definition of customized functions
5.4 Solution of Case Study 4
5.5 Exercises
6 Modeling with tuples
6.1 Tuple
6.1.1 Definition, declaration, and initialization
6.1.2 Linking tuples
6.1.3 Tuples in IBM ILOG Script
6.2 Tuple sets
6.2.1 Initialization
6.2.2 Decision variables
6.2.3 Key attributes
6.2.4 Linking tuple sets
6.2.5 Tuple sets in IBM ILOG Script
6.3 Efficient modeling with tuple sets
6.3.1 Slicing
6.3.2 Efficient access structures
6.3.3 Efficient and inefficient modeling of linked tuple sets
6.4 Solution of Case Study 4: Efficient modeling of sparse matrices by tuple sets
6.4.1 Modeling by a matrix
6.4.2 Modeling by a tuple set
6.4.3 Runtime comparison between matrix and tuple modeling for large data sets
6.5 Exercises
7 Separating model and data
7.1 Internal and external data initialization
7.2 Explicit initialization in data files
7.2.1 Basic model parameters
7.2.2 Arrays
7.2.3 Sets
7.2.4 Tuples
7.3 Connecting spreadsheet files
7.4 Solution of Case Study 5
7.5 Exercises
8 Selected features of OPL and CPLEX Optimization Studio
8.1 Modeling with logical operations
8.1.1 Logical operators in OPL
8.1.2 Modeling with logical operators
8.1.3 Example of using "==" as a logical versus relational operator
8.1.4 Conversion of logical operations into linear constraints
Linking binary variables
Linking binary variables and "real" constraints
8.1.5 Counting sums
8.2 Piecewise linear functions
8.2.1 Special case: step functions
8.2.2 General piecewise linear functions
Continuous functions
Functions with jump points
8.2.3 Modeling piecewise linear functions by linear constraints
General linearization techniques
Special cases
8.3 Conflicts and relaxations
8.3.1 Output in the Conflicts tab
8.3.2 Output in the Relaxations tab, Engine log tab, Solutions tab, and Problem browser
8.4 Solution of Case Study 6
8.5 Exercises
9 Selected features of IBM ILOG Script
9.1 Input and output with IBM ILOG Script
9.1.1 Reading from and writing to text files
9.1.2 Reading from CSV files
9.2 Pre- and Postprocessing with IBM ILOG Script
9.3 Runtime measurement and CPLEX settings
9.4 Solution of Case Study 7
9.5 Exercises
10 Flow control with ILOG Script
10.1 Overview and use cases
10.2 Accessing model and data files
10.3 Solution of Case Study 8
10.4 Reference to further implementation examples
10.5 Exercises
Part 2 Application studies
11 Covering location planning
Proposed solution
12 Fleet sizing
Proposed solution
13 Location planning
Proposed solution
14 Transportation planning
Proposed solution
15 Revenue Management
Proposed solution
16 Lot sizing
Proposed solution
Bibliography
Index

Citation preview

Graduate Texts in Operations Research Series Editors: Richard Boucherie · Johann Hurink

Stefan Nickel Claudius Steinhardt Hans Schlenker Wolfgang Burkart

Decision Optimization with IBM ILOG CPLEX Optimization Studio A Hands-On Introduction to Modeling with the Optimization Programming Language (OPL)

Graduate Texts in Operations Research Series Editors Richard Boucherie, University of Twente, Enschede, The Netherlands Johann Hurink, University of Twente, Enschede, The Netherlands

This series contains compact volumes on the mathematical foundations of Operations Research, in particular in the areas of continuous, discrete and stochastic optimization. Inspired by the PhD course program of the Dutch Network on the Mathematics of Operations Research (LNMB), and by similar initiatives in other territories, the volumes in this series offer an overview of mathematical methods for post-master students and researchers in Operations Research. Books in the series are based on the established theoretical foundations in the discipline, teach the needed practical techniques and provide illustrative examples and applications.

Stefan Nickel • Claudius Steinhardt Hans Schlenker • Wolfgang Burkart

Decision Optimization with IBM ILOG CPLEX Optimization Studio A Hands-On Introduction to Modeling with the Optimization Programming Language (OPL)

Stefan Nickel Institute for Operations Research Karlsruhe Institute of Technology Karlsruhe, Germany Hans Schlenker Munich, Germany

Claudius Steinhardt University of the Bundeswehr Munich Neubiberg, Germany Wolfgang Burkart University of Augsburg Augsburg, Germany

It is a revised, extended and translated edition: “Angewandte Optimierung mit IBM ILOG CPLEX Optimization Studio” by Stefan Nickel et al., © Springer-Verlag GmbH Deutschland 2021. Published by SpringerGabler. All Rights Reserved. ISSN 2662-6012 ISSN 2662-6020 (electronic) Graduate Texts in Operations Research ISBN 978-3-662-65480-4 ISBN 978-3-662-65481-1 (eBook) https://doi.org/10.1007/978-3-662-65481-1 © Springer-Verlag GmbH Germany, part of Springer Nature 2021, 2022 This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed. The use of general descriptive names, registered names, trademarks, service marks, etc. in this publication does not imply, even in the absence of a specific statement, that such names are exempt from the relevant protective laws and regulations and therefore free for general use. The publisher, the authors, and the editors are safe to assume that the advice and information in this book are believed to be true and accurate at the date of publication. Neither the publisher nor the authors or the editors give a warranty, expressed or implied, with respect to the material contained herein or for any errors or omissions that may have been made. The publisher remains neutral with regard to jurisdictional claims in published maps and institutional affiliations. This Springer imprint is published by the registered company Springer-Verlag GmbH, DE, part of Springer Nature. The registered company address is: Heidelberger Platz 3, 14197 Berlin, Germany

Preface

This book is based on material from a course that we have taught and continuously developed since 2012. The course connects with advanced undergraduate and graduate modules for students in management science, mathematics, computer science, industrial engineering, and economics and is offered at the Karlsruhe Institute of Technology, the University of Augsburg, and the University of the Bundeswehr Munich. The book provides a sound introduction to working with IBM ILOG CPLEX Optimization Studio and, in particular, to using the OPL modeling language for modeling linear and integer linear optimization problems. It is intended for practitioners involved in the modeling and optimization of planning and decision-making problems, as well as instructors and students at universities and colleges to use in lectures and courses. No prior knowledge of the mathematical foundations of optimization is assumed, although this book can be used to teach a comprehensive course that would supplement an introductory course to operations research. Part 1 of the book consists of 10 sequential lessons and is supplemented by a selection of small exercises at the end of each chapter to practice the content learned. Part 2 contains a series of larger application studies, including extensive, didactically prepared solution suggestions. Further details on the structure of the book can be found in Section 1.4. The book's accompanying website www.opl.education offers supplementary materials, such as exercise solutions and files with all the program codes presented in the book. The course was developed in close coordination with IBM Germany. We would like to thank Dr. Martin Mähler, Manager University Relations at IBM, for the cooperation, for the provision of free academic licences, and for IBM’s financial support of the book V

VI

Preface

project. We would also like to thank Mr. Christian Rauscher and Mrs. Jialin Yan from the publishing house Springer Gabler for their excellent cooperation. Moreover, we express our sincere thanks to the academic staff and assistants of the three participating chairs for their support during the preparation of the manuscript and especially for proofreading our work.

Karlsruhe, Munich, and Augsburg March 2022

Stefan Nickel Claudius Steinhardt Hans Schlenker Wolfgang Burkart

Preface

Prof. Dr. Stefan Nickel Karlsruhe Institute of Technology (KIT) Institute for Operations Research (IOR) Chair of Discrete Optimization and Logistics Kaiserstrasse 89 76133 Karlsruhe [email protected]

Prof. Dr. Claudius Steinhardt University of the Bundeswehr Munich (UniBw M) Department of Economics and Management Chair of Business Analytics and Management Science Werner-Heisenberg-Weg 39 85577 Neubiberg [email protected]

Dr. Hans Schlenker Wallbergstraße 28 81539 Munich [email protected]

Dr. Wolfgang Burkart University of Augsburg Institute for Statistics and Mathematical Economic Theory Chair of Analytics and Optimization Universitätsstraße 16 86159 Augsburg [email protected]

VII

Table of contents

Preface .............................................................................................................................. V List of abbreviations .................................................................................................... XIII Part 1 Lessons Chapter 1: Introduction ...................................................................................................3 1.1 Modeling and solving planning and decision-making problems .................................. 4 1.2 IBM ILOG CPLEX Optimization Studio .......................................................................... 4 1.3 Introductory example .......................................................................................................... 5 1.4 Content covered in the book .............................................................................................. 7 Chapter 2: IBM ILOG CPLEX Optimization Studio—A primer ...................................9 2.1 Licenses and installation...................................................................................................... 9 2.2 Starting the Studio .............................................................................................................. 10 2.3 First steps ............................................................................................................................. 12 2.4 Studio Help and other resources ...................................................................................... 20 Chapter 3: Setting up a model ........................................................................................ 23 3.1 Declaration and initialization of model parameters ...................................................... 25 3.2 Declaration of decision variables ..................................................................................... 28 3.3 Objective function .............................................................................................................. 30 3.4 Constraints .......................................................................................................................... 32 3.5 Solution of Case Study 1 .................................................................................................... 34 3.6 Exercises .............................................................................................................................. 37

IX

X

Table of contents

Chapter 4: Data structures and related OPL language elements.................................. 39 4.1 Ranges .................................................................................................................................. 40 4.2 Arrays ................................................................................................................................... 41 4.3 Sets ........................................................................................................................................ 46 4.4 Aggregate operators ........................................................................................................... 51 4.5 The forall-quantifier .......................................................................................................... 54 4.6 Solution of Case Study 2 .................................................................................................... 56 4.7 Exercises .............................................................................................................................. 58 Chapter 5: Introduction to IBM ILOG Script ............................................................... 61 5.1 Structure and basic language elements of IBM ILOG Script ....................................... 62 5.2 OPL data elements and IBM ILOG script variables ...................................................... 64 5.3 Other language elements ................................................................................................... 66 5.4 Solution of Case Study 4 .................................................................................................... 74 5.5 Exercises .............................................................................................................................. 76 Chapter 6: Modeling with tuples ................................................................................... 79 6.1 Tuple .................................................................................................................................... 80 6.2 Tuple sets ............................................................................................................................. 85 6.3 Efficient modeling with tuple sets .................................................................................... 95 6.4 Solution of Case Study 4: Efficient modeling of sparse matrices by tuple sets ....... 102 6.5 Exercises ............................................................................................................................ 109 Chapter 7: Separating model and data ........................................................................ 113 7.1 Internal and external data initialization ........................................................................ 115 7.2 Explicit initialization in data files ................................................................................... 118 7.3 Connecting spreadsheet files .......................................................................................... 123 7.4 Solution of Case Study 5 .................................................................................................. 127 7.5 Exercises ............................................................................................................................ 131 Chapter 8: Selected features of OPL and CPLEX Optimization Studio..................... 135 8.1 Modeling with logical operations ..................................................................................... 137 8.2 Piecewise linear functions ............................................................................................... 147 8.3 Conflicts and relaxations ................................................................................................. 160 8.4 Solution of Case Study 6 ................................................................................................. 166 8.5 Exercises ............................................................................................................................ 168 Chapter 9: Selected features of IBM ILOG Script ....................................................... 173 9.1 Input and output with IBM ILOG Script ...................................................................... 174 9.2 Pre- and Postprocessing with IBM ILOG Script .......................................................... 181 9.3 Runtime measurement and CPLEX settings ................................................................ 183 9.4 Solution of Case Study 7 .................................................................................................. 186 9.5 Exercises ............................................................................................................................ 189

Table of contents

XI

Chapter 10: Flow control with ILOG Script ................................................................ 193 10.1 Overview and use cases ................................................................................................... 195 10.2 Accessing model and data files ....................................................................................... 196 10.3 Solution of Case Study 8 ................................................................................................. 200 10.4 Reference to further implementation examples ........................................................... 204 10.5 Exercises ............................................................................................................................ 206 Part 2 Application studies Chapter 11: Covering location planning ..................................................................... 211 Chapter 12: Fleet sizing ................................................................................................219 Chapter 13: Location planning .................................................................................... 227 Chapter 14: Transportation planning .......................................................................... 237 Chapter 15: Revenue Management .............................................................................. 247 Chapter 16: Lot sizing...................................................................................................257 Bibliography .................................................................................................................. 275 Index .............................................................................................................................. 277

List of abbreviations

cf.

compare

CM

contribution margin

CPLEX (solver)

solver for mathematical optimization problems

CPLEX Optimization Studio

IBM ILOG CPLEX Optimization Studio

i.e.

that is

ibid.

in the same source

e.g.

for example

etc.

et cetera

EUR

euro

ff.

and the following pages

GB

gigabyte

OPL

Optimization Programming Language

Studio

graphical interface of the IBM ILOG CPLEX Optimization Studio software

Studio Help

help system of the IBM ILOG CPLEX Optimization Studio

USD

US dollar

vs.

versus, as opposed to

XIII

Part 1 Lessons

1

Introduction

Big Data, Artificial Intelligence, Business Analytics, Data Science—every major company today is dealing with these or related topics in order to remain competitive in the "digital world" or even to implement completely new, disruptive business models. All of these topics are connected to companies looking for new ways to profitably exploit the immense amounts of data available for management decisions and to proceed as "intelligently" and in a way that is as automated as possible. This affects all levels of decision-making—from strategic to operational—as well as all corporate and business functions, whether general management, finance, marketing, or operations. However, in most cases it is not enough just to intelligently evaluate the existing data by using the most sophisticated data-mining methods—for example, from machine learning or statistics—to identify patterns and anomalies or to derive forecasts, for example. The point is that, ultimately, definitive decisions have to be made—and even with the most intelligent analysis at hand, they can still be complex and impossible for individuals to oversee. Often, the reason for failure is that not all decision options can be explicitly considered and evaluated against each other because their number is very large or even infinite. According to a modern understanding of business analytics, descriptive and predictive methods should be closely linked with prescriptive analytics—i.e., the data-based, automated generation of optimized recommendations in complex decision-making situations. It is precisely for this purpose of decision support that "Operations Research" has established itself over the past 80 years as a highly developed methodological discipline at the interface between management, economics, mathematics, and computer science.

© Springer-Verlag GmbH Germany, part of Springer Nature 2022 S. Nickel et al., Decision Optimization with IBM ILOG CPLEX Optimization Studio, Graduate Texts in Operations Research, https://doi.org/10.1007/978-3-662-65481-1_1

3

4

1.1

1 Introduction

Modeling and solving planning and decision-making problems

In Operations Research, mainly mathematical optimization techniques are used in the context of decision support—such as linear or integer linear optimization. In so doing, an abstract simplification, i.e., a model of reality, is used. Depending on the complexity of the underlying problem, this model—in a structured form—simplifies reality because not all potentially relevant contexts and objectives can or should always be included, and possibly not all decision-relevant data can or should be captured. The discipline is more relevant today than ever before. In particular, the high computing power, the large storage capacity, and the availability of granular data in many companies make optimization techniques applicable on a large scale and with very complex and detailed models, which only existed "in theory" a few years ago. In the context of optimization, the two aspects of modeling and solving are distinguished to answer a business problem, which in operations research is also called a planning problem or a decision-making problem. The problem must first be transformed into a mathematical-formal description (modeling). The resulting model (also called the optimization model) is formulated in such a way that it contains decision variables that can be influenced. Subsequently, the best possible or even optimal values of the decision variables are determined for the model using appropriate methods that take the objective into account (solving). The decision variables are transferred back to reality and applied.

1.2

IBM ILOG CPLEX Optimization Studio

Numerous powerful software packages are available for both modeling and solving planning and decision-making problems. While in the past research focused on the individual development of customized, problem-adapted solution methods, today's computer performance and advances in optimization technology make it possible to solve many problems with the generic standard methods that such software has implemented. Consequently, it is often enough to focus on modeling. This book deals with the IBM ILOG CPLEX Optimization Studio (in short: CPLEX Optimization Studio), a widely used software package for modeling and solving linear and integer linear optimization problems. The starting point for the development of the CPLEX Optimization Studio was the CPLEX solving engine or solver, which has been in continuous development since the end of the 1980s. Especially in the past two decades, there have been significant improvements in efficiency, which, together with faster hardware, result in immense runtime advantages. There are impressive examples of models that in 1998 took CPLEX a whole day to solve on a then-current computer but that can be solved in

1.3 Introductory example

5

less than 5 seconds on a computer today. Likewise, it is now possible to solve model instances whose solvability seemed completely unthinkable just 20 years ago. The CPLEX Optimization Studio complements CPLEX, as well as another solver (which is based on constraint programming techniques but is not discussed in this book), with convenient modeling features. At its core is the modeling language OPL. Pascal Van Hentenryck invented and developed it in the 1990s. OPL stands for Optimization Programming Language and enables intuitive model development in purely descriptive form, which on the one hand is close to the usual mathematical formulation but on the other hand is also easily accessible for non-mathematicians. This is one of the reasons why, in addition to its original use for purely academic purposes and in prototypes, it is now used in many productive applications in companies. Problems from a wide variety of areas are addressed, such as production, marketing, and logistics.

1.3

Introductory example

To give a first impression of the basic type of planning and decision-making problems to be solved with the help of the CPLEX Optimization Studio, we present an introductory case study from the context of the production and distribution of bicycles. The example is fictitious and reduced to the essentials but is nevertheless realistic in many respects. For example, in some real applications (e.g., in automobile manufacturing), very similar tasks exist and similar models are used to obtain solutions. At the same time, the example introduces the context of bicycle production, which is uniformly taken up in almost all examples and case studies in this book. As part of production planning, the planner must make several decisions. The result is a production plan as the "sum" of all definite decisions made—i.e., the determination of which location can be used to produce which types of bicycles and how many. The integrated consideration of transport makes the task more challenging. These challenges go hand in hand with production: If there is demand for bicycle types in a market without a local plant, or if there is a plant, but it does not have sufficient production capacity to manufacture all the bicycle types demanded, these bicycle types must be manufactured at another plant and then transported to the target market. Transport costs are incurred accordingly. Alternatively, it could also make sense not to meet demand or to meet it only partially. The central objective of the simplified production planning in the example is to maximize the total contribution margin—that is, the difference between revenues and variable costs. Revenue is achieved through sales—i.e., through the unit revenue achieved multiplied by the sales quantity, which is the minimum of demand and number of units provided in the respective markets. In the example, costs are incurred through production

6

1 Introduction

and transport. However, they are at odds with each other: Decisions that reduce (global) production costs often generate higher transport costs and vice versa. Introductory Example: Integrated production and transport planning "RideEasy" is a global company that produces bicycles. For this purpose, it operates a total of five plants in five regions North America (NA for short), South America (SA), Europe (EU), Africa (AF), and Asia (AS).

It offers three different types of bicycles to be produced in the plants and demanded in the regions. A total of five machines are required for production. Machines 3, 4, and 5 are only required for specific bicycle types. Each region has a known demand for different types of bicycles for the planning period, each of which can be served from all existing plants. Transportation costs are incurred in the process. The demand does not have to be completely satisfied, so the generated sales revenue reflects the actual demand served. All the necessary details (machine capacities, resource consumption per bicycle type, demand, unit sales revenue, transportation cost per unit, etc.) have been collected and are available in detailed lists. The problem now lies in planning how many bicycles of which model are to be produced at which plant. At the same time, we need to determine which types of bicycles should be supplied to which region, from which plant, and to what extent. In so doing, the achievable total contribution margin has to be maximized.

As part of production planning, the planner must make several decisions. The result is a production plan as the "sum" of all definite decisions made—i.e., the determination of which location can be used to produce which types of bicycles and how many. The integrated consideration of transport makes the task more challenging. These challenges go hand in hand with production: If there is demand for bicycle types in a market without a local plant, or if there is a plant, but it does not have sufficient production capacity to

1.4 Content covered in the book

7

manufacture all the bicycle types demanded, these bicycle types must be manufactured at another plant and then transported to the target market. Transport costs are incurred accordingly. Alternatively, it could also make sense not to meet demand or to meet it only partially. The central objective of the simplified production planning in the example is to maximize the total contribution margin—that is, the difference between revenues and variable costs. Revenue is achieved through sales—i.e., through the unit revenue achieved multiplied by the sales quantity, which is the minimum of demand and number of units provided in the respective markets. In the example, costs are incurred through production and transport. However, they are at odds with each other: Decisions that reduce (global) production costs often generate higher transport costs and vice versa. Finally, the restrictions of planning must also be taken into account. In particular, in the simplified example, the given production capacities must be considered a limiting factor: The plants cannot manufacture more products than the available resources—in this case, the machines—allow. All in all, it is clear that even this small illustration represents a complex planning task with many interdependencies for which, in general, it is not possible to determine a definitive production plan with the largest possible contribution margin simply "by taking a sharp look." Therefore, in this book, possibilities are introduced for transferring such problems with OPL into a model and then solving corresponding model instances—i.e., for determining an optimal solution for the task by finding values for existing variables such as revenues, capacities, etc. For this purpose, as will be explained in more detail later, the objective is formally represented by an objective function and the constraints by (in)equations.

1.4

Content covered in the book

This book provides a solid introduction to working with the CPLEX Optimization Studio and, in particular, using the OPL language for modeling linear and integer linear optimization problems. The central language elements of OPL are taught and their use is demonstrated by examples. No prior knowledge of the mathematical foundations of linear and integer linear optimization is required—however, for the reader who is interested or has prior knowledge, we discuss the appropriate formal mathematical notations in separate boxes. Likewise, this introductory book does not systematically discuss the solution procedures of linear and/or (mixed) integer linear optimization, such as those implemented in CPLEX. Nevertheless, a basic understanding can be useful here, if there are different modeling possibilities for the same problem since the kind of modeling can have a critical impact on the runtime performance of the solution process. Appropriate examples are discussed at various points throughout the book to provide some clarity. In the book, we use only linear models.

8

1 Introduction

Interested readers are referred to the comprehensive introductory works on operations research by, e.g., Hillier and Lieberman (2015), Taha (2017), or Winston (2004) for more detailed presentations of the mathematical-methodological foundations. The book is divided into two parts. Part 1 contains the lessons. Chapters 2 to 5 provide the elementary basics to get started with the modeling work. Building on this, Chapters 6 to 10 contain more advanced concepts and modeling techniques. Each chapter dealing with language elements opens with a case study. In each case, the language elements that are introduced are required to work on the study. In this way, a solution can be developed and presented by the end of the particular chapter. To create a uniform context, all case studies move thematically within the fictitious world of "bicycle production" as introduced in Section 1.3. The other examples contained in the chapters are also predominantly based on this context. Short exercises at the end of each chapter enable the reader to practice the concepts introduced in the foregoing exposition. Most of these exercises pick up on the examples covered in the respective chapters. The corresponding solutions are available on the book's website. Part 2 of the book contains a series of additional, larger application studies, for the processing of which much of the content introduced in Part 1 is applied. In this way—again in the context of bicycle production—various typical topics of Operations Research (e.g., location planning, transportation planning, lot sizing, and revenue management) are covered. We pay special attention to detailed, didactically prepared solution proposals. These also discuss, in part, the mathematical modeling for the problems to be solved or demonstrate different, alternative solution approaches with their advantages and disadvantages. The book's target readers are practitioners and students, who can also use it for selfstudy. In teaching at universities and colleges, it is particularly suitable for introductory courses in advanced bachelor studies in management, economics, and mathematics as well as computer science. Lecturers can request corresponding course material, which takes up the individual lessons of the book, via the website that accompanies the book. There, readers will also find the solutions to the exercises from Part 1 and all program code examples in the appropriate file format, as well as additional material for download.

2

IBM ILOG CPLEX Optimization Studio—A primer

This chapter introduces the Integrated Development Environment (IDE), which is central to model development and which, in short, is referred to as Studio. It explains how to install CPLEX Optimization Studio on a computer, how to start the Studio, which basic functionalities and components are available there, how to create projects, add optimization models, and have them solved. After studying this chapter, readers should be able to use the software to import or reimplement and run the examples in this book. In this respect, this chapter provides a quick and targeted introduction but by no means covers all the functionalities of the Studio. To learn the other functionalities and as a reference, we recommend reading the electronic help texts, which are available in the Studio’s integrated help system, referred to as Studio Help (cf. Section 2.4).

2.1

Licenses and installation

CPLEX Optimization Studio is available in different editions under different licensing agreements. In addition to the full edition, there is a Community Edition and an academic edition. IBM provides the Community Edition free of charge as a download. It is technically limited to smaller optimization models but sufficient for many of the examples and exercises given in this book. The academic edition has the same functionality as the full version, and as part of the IBM Academic Initiative, IBM provides it free of charge to academic institutions for research and teaching. All three types of licenses cover the development of models and applications in a wide variety of contexts. In addition, there are deployment licenses for servers and medium-sized workstations for productive use. The descriptions in this book are based on version number 22.1, which was the most up-to-

© Springer-Verlag GmbH Germany, part of Springer Nature 2022 S. Nickel et al., Decision Optimization with IBM ILOG CPLEX Optimization Studio, Graduate Texts in Operations Research, https://doi.org/10.1007/978-3-662-65481-1_2

9

10

2 IBM ILOG CPLEX Optimization Studio—A primer

date version at the time of this writing, and which applies to all the editions mentioned before. CPLEX Optimization Studio is available for the Windows, Linux, and macOS operating systems. The Linux and macOS releases’ range of functions is limited compared with those of the Windows release, but most of the features are identical. Differences can also be found in the operation—such as the start of the Studio. The description in this book always refers to the English Windows 10 release. For installation in Windows 10, the .exe installation file of CPLEX Optimization Studio is required. A double-click then starts the selfexplanatory installation program. By default, the Studio starts in the language preset in the operating system. The explanations in this book are based on the English language setting. It is also possible to start the Studio in many other languages, e.g., in Spanish (cf. Tip 1). Tip 1

Change of the user language

To convert, e.g., to the Spanish language, the file "oplide.ini" must be edited and adapted. This is located in the installation directory, whose path was specified during installation, under: \oplide.ini The following lines must be inserted at the beginning of this file by using a text editor, with "es" indicating Spanish: -nl es After the file is saved, the next restart of the Studio will be in Spanish. It works in the same way for other languages, using the corresponding country abbreviation.

2.2

Starting the Studio

After CPLEX Optimization Studio has been installed as described above, the Studio can be run as a desktop app in Windows 10 via the start menu. To do so, open the CPLEX Studio IDE 22.1.0 entry in the CPLEX Optimization Studio 22.1.0 folder (cf. Fig. 2.1).

2.2 Starting the Studio

11

Figure 2.1 Starting the Studio from the start menu

After the first start, the program window of the Studio shows the welcome screen (cf. Fig. 2.2). This screen is only displayed when the Studio is started for the first time; otherwise, the Studio always opens in the last layout used. However, the welcome screen can still be opened from the program menu ("Help > Welcome"). The welcome screen offers different ways of entering the Studio via the four icons displayed. Clicking on the first icon ("globe") opens a specific page from the Studio Help (cf. Section 2.4), which is addressed to users who have experience in Operations Research but have not worked with the Studio before. For them, a few examples here give a quick introduction to the software’s features.

Figure 2.2 The welcome screen of the Studio

12

2 IBM ILOG CPLEX Optimization Studio—A primer

The second symbol ("note") links to a folder with various tutorials within the Studio Help. Case studies are used to introduce the various functionalities of the Studio, as well as the selection of OPL language elements. The third icon ("objects") also leads to the Studio Help—more specifically, to detailed explanations of some modeling examples (samples). These model files are located at \opl\examples\opl and are easy to access or import (cf. Section 2.3.1). The fourth symbol ("star") informs readers about new features (what's new) and links to current postings in a corresponding online forum. A click on the arrow in the upper right corner closes the welcome screen and opens the workbench, which is empty the first time it is started (cf. Fig. 2.3).

Figure 2.3 Studio’s workbench when the application is opened the first time

2.3

First steps

The work with the Studio is organized into OPL projects. An OPL project is a set of files that are grouped together—e.g., because they belong to a particular optimization problem. In the Studio, all projects are usually managed in a workspace, which corresponds to a specific working directory on the hard disk. However, it is also possible to store projects outside the workspace and, thus, in different locations on the hard disk. Tip 2

Eclipse Platform

The Studio is built on the Eclipse platform, a generic development environment. Many of the concepts presented here—such as projects, working with windows, editors, etc.—are standards from Eclipse. There are many tutorials on the Internet about working with Eclipse, which also apply to the Studio.

2.3 First steps

13

2.3.1 Importing a sample project As already mentioned, the CPLEX Optimization Studio comes with a number of modeling examples. The corresponding projects can be conveniently imported into one’s own workspace in the Studio, as illustrated in Fig. 2.4.

Figure 2.4 Three steps to import a sample project into a workspace

Importing a sample project requires three steps. From the program menu, select "File > New > Example" or "File > Import > Example." Then, from the opening window, select "IBM ILOG OPL Examples" and click "Next." Now, the example to be imported can be selected; either the catalog tree or the search field can be used for this purpose. For illustration, in the following, we import an example of optimization in gas production. To do this import, enter the term "gas" in the search field, select the example, specify the destination for storing the project (usually the Workspace), and confirm with "Finish." The example "gas" has now been imported, and the elements of the new project are displayed in the OPL projects window at the top left (cf. Fig. 2.5).

14

2 IBM ILOG CPLEX Optimization Studio—A primer

Figure 2.5 Studio after a sample project has been imported

2.3.2 The components of a project Fig. 2.5 shows that the project "gas"—like all projects—is organized into a tree structure. On the top level, it contains the branch "Run Configurations," as well as the four files gas.mod, gas1.mod, gas.dat, and gasn.dat. These have the following meanings: ∂ Model files: Files with the extension .mod are OPL model files. Each of them usually contains a specific optimization model that is generically formulated using model parameters. The definitive values of the model parameters—i.e., the data— can also be defined in a separate place within the model file. ∂ Data files: Alternatively, OPL also allows users to separate models and data, which brings great advantages if the same model is to be solved with several alternative parameter configurations—that is, different model instances are to be formed. Files with the extension .dat are used to define such data (cf. Chapter 7). ∂ Run configurations: The Studio’s project structure allows multiple parts of an optimization model to be distributed across multiple model files. Similarly, data can be defined in multiple data files. Further, different data sets—e.g., with different values for demand and cost—can be represented by several different data files that can be used alternatively. Thus, to precisely define a model instance to be solved, as well as the underlying data to be used, a run configuration is used. It brings together a specific subset of the files in the project. In Fig. 2.5, we expand the project tree of the project "gas" to show the entry "run configurations" and, among others, the entry "No external Data." This run configuration contains only the model file "gas1.mod," which can be used without a separate data file. Each of the other two configurations, "Default Configuration" and "Indices in arrays," contains the model file "gas.mod" but they differ in the data files used (gas.dat and gasn.dat,

2.3 First steps

15

respectively). It should be noted that the elements of a run configuration are not copies of the corresponding files from the top tree level but merely refer to them. ∂ Settings files: In addition, there are OPL settings files with the file extension .ops, with which technical configurations of the used solver can be made. These should be applied to the project during model solving. In this example and throughout this textbook, the default settings are always used; therefore, settings files are not required. Tip 3

Directory of a project on the hard disk

Each project is stored on the hard disk of the computer in its own folder—usually, in subdirectories of the workspace. The project folder’s path can be retrieved by right-clicking on the particular project in the "OPL Projects" window, under "Properties > Resource > Location" in the context menu that opens. 2.3.3 Solving a model instance To solve a model instance (i.e., to start the optimization or the solution process with the CPLEX solver), select the corresponding run configuration in the "OPL Projects" window and right-click on it. In the context menu that appears, the optimization is started with "Run this." Fig. 2.6 illustrates the procedure for the selected example using the run configuration "No external data." We discuss the Studio’s elements for displaying and analyzing the optimization result in Section 2.3.5.

Figure 2.6 Execution of a run configuration

Table 2.1 shows the different files and their roles in the context of the solution process of a model instance, with the use of data and settings files only optional in the circumstances we explained.

16

2 IBM ILOG CPLEX Optimization Studio—A primer

Table 2.1 Execution of an optimization with CPLEX Optimization Studio

Step

Associated file

1. Load model 2. Load data 3. Data preprocessing 4. Execute solver (CPLEX) 5. Data postprocessing 6. Publish results

Model file (.mod) Data file (.dat) Model file (.mod) Settings file (.ops) Model file (.mod) Data file (.dat)

Tip 4

oplrun.exe

For large models or models with a lot of data, it often makes sense to do the model solving outside the Studio. Doing so allows CPLEX to use more main memory. The optimization itself also runs faster than inside the Studio because less debugging information (i.e., information to find and fix errors in the program code) is generated. For this purpose, there is the "oplrun.exe" program. It can be started from a Windows command line window by changing to the directory of the project "gas" (cf. Tip 3) and entering "oplrun gas1.mod". This starts the program oplrun.exe for the model gas1.mod and generates the output of Fig. 2.7. The output can also be written directly to a log file by appending the command ">>" followed by the name of the desired file (e.g., "oplrun gas1.mod >> log.txt"). Further, with "oplrun.exe" it is possible to execute several test cases automatically one after the other by embedding corresponding calls of the program into a Windows batch file.

Figure 2.7 Running oplrun in a Windows command prompt (cmd.exe)

2.3 First steps

17

2.3.4 Studio components After optimization, the Studio elements are filled with data and content. Fig. 2.8 shows the default layout of the corresponding windows. However, this layout can be flexibly adapted (e.g., with drag-and-drop), and additional elements can be added via the program menu under "Window > Show View." The original layout can be restored via "Window > Reset Perspective." Tip 5

Quickly zoom in and out of windows

The Studio contains many elements that often need to be displayed at the same time. As a result, some windows become too small to reasonably work with. Therefore, individual subwindows of the Studio can be temporarily enlarged to the full size of the main window by double-clicking the window title: Double-clicking the "gas.mod" window will enlarge it, and the reverse effect can be achieved by double-clicking the window title again.

Figure 2.8 Studio after execution of an optimization

Specifically, the following elements are displayed by default: ∂ OPL Projects: The "OPL Projects" window displays all the projects that have ever been imported into or created in this workspace. The projects can be managed here, and individual files can be opened for editing with a double-click. The appropriate editor then opens in the "Model Editor" window (cf. below). Further, projects can be removed from this window by right-clicking and selecting "Delete" in the context

18

2 IBM ILOG CPLEX Optimization Studio—A primer









menu that opens. This menu also enables the user to select whether they want to delete the associated project folder from the hard disk. Model Editor: In the “Model Editor” (window in the top center), all files belonging to a project can be edited. For model files, a text editor is available that supports colored syntax highlighting. Any number of files can be opened in parallel, and all open files remain quickly accessible via corresponding tabs (from left to right). Outline: The “Outline” window displays the structure of the model or the data of the current file in a tree. Here the developer gets an overview of the model and can quickly access the definitions of the individual model components. By clicking on an entry, the model editor jumps to the corresponding text passage in the file. Problem Browser: After solving a model instance, the "Problem Browser" displays the elements of the optimization model. In addition to a systematic representation of the (input) data and the constraints, this includes the optimal values of the decision variables obtained from the optimization, as well as—in the drop-down list " Solution with objective …" at the top—the optimal value of the objective function. Analysis of Models, Results, and the Solution Process: At the bottom right, a series of windows displays different kinds of information, mainly after optimization (cf. Section 2.3.5).

2.3.5 Analysis of models, results, and the solution process As shown in Fig. 2.8, the Studio provides a range of information after optimization, which allows the developer to analyze the solution process and the results. The corresponding windows can be displayed via tabs. A tab’s title is displayed in bold if the associated window displays new information and, therefore, might be of interest to the user. If, on the other hand, the title is displayed in normal print, the optimization has not generated any (new) information. The following elements can be retrieved: ∂ Problems: The "Problems" tab is the only one among the explained elements that is already important during the creation of the model because here syntax errors made in the program code are indicated and explained directly. Thus, this already happens before solving the model by a corresponding run configuration. A doubleclick on the respective error message jumps to the erroneous line within the model editor. Further, problems occurring during the model solution—such as memory overflow, data access error, etc.—are displayed. ∂ Scripting log: In the "Scripting log" tab an output with the scripting language IBM ILOG Script can be generated during the solution process (cf. Chapter 5 and Chapter 9). ∂ Solutions: After optimization, the "Solutions" tab displays information about both the obtained solution and the solution process in textual form. ∂ Conflicts and Relaxations: If the constraints of the optimization model are incompatible and there is no solution, CPLEX can automatically generate a minimal

2.3 First steps

19

subset of incompatible constraints. This means that omitting one of the displayed constraints would resolve the incompatibility—unless there are multiple independent incompatibilities (cf. Chapter 8). This subset is then displayed in the "Conflicts" tab. If there are conflicts, CPLEX can also determine a sufficient minimum required change in constraints or bounds that will, in turn, make the problem feasible. This is displayed in the "Relaxations" tab. ∂ Engine log, Statistics, Profiler, and Watson Machine Learning: The four remaining elements provide mostly technical information to the advanced user. The "Engine log" tab provides a textual output of information from the CPLEX solver during the solution process. The "Statistics" tab contains a tabular representation of some key metrics of the optimization model (e.g., number of variables and constraints, among others), as well as a graphical visualization of runtime versus solution quality. The "Profiler" provides an interactive table for analyzing runtimes, memory requirements, and so forth during the different phases of the solution process. The “Watson Machine Learning” tab refers to a connector that makes it possible to run models on the cloud. Detailed information on all elements is contained in the Studio Help (cf. Section 2.4). 2.3.6 Creating a new project and importing existing projects Besides the possibility to import a sample project (cf. Section 2.3.1) and use it as a basis for modeling, an entirely new project can be created (cf. Fig. 2.9). To do this, select "File > New > OPL Project" from the program menu. In the window that opens, the desired project name (in Fig. 2.9 the name "G1-ILOG") and the location must be specified. In addition, by setting the corresponding check marks, a model file, a data

Figure 2.9 Creating a new project

20

2 IBM ILOG CPLEX Optimization Studio—A primer

file, a settings file, and a run configuration can be added. A click on "Finish" creates the project. An already existing project can be imported by selecting "File > Import > Existing OPL projects" in the program menu (cf. Fig. 2.10). Then, under "Select root directory," the directory on the hard disk in which the project folder is located can be selected. "Projects" will then automatically show all projects that can be found there. After selecting the project to be imported, you can also specify whether the project is to be copied to the workspace or the original directory is to be retained. In the latter case, the original project is overwritten during further modeling. The described importing of projects is also helpful when users are working in a team or on different computers. New components—in particular, model files, data files, settings files, and run configurations—can easily be added to existing projects. This can be done, e.g., via the context menu by right-clicking on the relevant project (New) or via "File > New."

Figure 2.10 Importing an existing project

2.4

Studio Help and other resources

A help system (referred to as Studio Help) is installed together with the software. It can be accessed from the program menu of the Studio under "Help > Help text content." This menu command opens the central help page (cf. Fig. 2.11). For further reading, we recommend a detailed description of the Studio, which you can find in the Studio Help under "IDE and OPL > CPLEX Studio IDE" (cf. Fig. 2.12).

2.4 Studio Help and other resources

21

Figure 2.11 The start page of the Studio Help

There are many pages on CPLEX, OPL, etc. available online—not only on the website of the vendor IBM. Here, special reference should be made to the online forums of IBM Developerworks, where questions are asked and solutions discussed.

Figure 2.12 Studio Help with a detailed description of the Studio

3

Setting up a model

As we pointed out in Chapter 2, optimization problems are formulated in model files (file extension .mod). In these files, the optimization problems are represented by corresponding mathematical models using IBM ILOG’s programming language OPL. The resulting program code consists of a sequence of OPL statements (a.k.a. OPL commands) in text format that can be entered comfortably with the help of the Studio as a development environment (cf. Chapter 2). OPL is a purely declarative programming language; in contrast with imperative programming languages such as Java, Python, or C, it requires only a description of the (optimization) problem to be solved. The routines for solving the problem (i.e., for the actual optimization) do not have to be explicitly programmed. The CPLEX solver does this automatically based on the model file. In addition, the imperative programming language IBM ILOG Script (formerly OPL Script) exists as a supplement to OPL, which—among other things—can also be used to intervene in the problem-solving process. The basics of ILOG Script will be discussed in Chapter 5. A model file usually consists of the following four sequential components: ∂ ∂ ∂ ∂

Declaration and initialization of model parameters Declaration of decision variables Objective function Constraints

All elements of the model file that precede the objective function are executed before the optimization—during preprocessing. Here, the order of the individual OPL statements plays an important role, since they are evaluated successively (e.g., the structure of Example 3.5, Example 3.8, etc., and Section 7.1). The main purpose of preprocessing is to validate input data and to convert the data to suit the construction of the model. Sections 6.3 and 6.4 discuss © Springer-Verlag GmbH Germany, part of Springer Nature 2022 S. Nickel et al., Decision Optimization with IBM ILOG CPLEX Optimization Studio, Graduate Texts in Operations Research, https://doi.org/10.1007/978-3-662-65481-1_3

23

24

3 Setting up a model

how additional data types can be generated during preprocessing, which can considerably speed up the construction of the model and, thus, its overall runtime. Also, IBM ILOG Script (cf. Chapters 5 and 9) is frequently used in preprocessing. All elements of the model file located after the constraints are executed after the optimization—that is, during postprocessing. Here, too, the language elements are processed in sequence. Typical postprocessing steps include the processing and output of optimization results, as well as the output of the solver’s status information or of decision variables or constraints. IBM ILOG Script is also frequently used in postprocessing. The following sections explain the different components of a model file in more detail and introduce the basic elements and keywords of OPL. Their application is discussed in the context of the following case study. We present the case study’s overall solution in Example 3.16 at the end of the chapter.

RideEasy Case Study 1: Production Planning in North America In the first case study, we consider only the North American plant of the company "RideEasy." In this plant, the two bicycle types U-A (bicycle type 1) and U-B (bicycle type 2) are to be produced. For this purpose, three machines (i.e., M1, M2, and M3) are required. Table 3.1 shows the machine time (capacity) available within the planning period for the individual machines, as well as the resource consumptions per bicycle type to produce one bicycle (consumption U-A and consumption U-B). Table 3.2 contains the demand for the two types of bicycles for the planning period, as well as the contribution margins per unit, which are referred to as CMs in the following exposition. The objective is to find an optimal (i.e., CM-maximizing) production program under the given machine restrictions, whereby demand does not have to be fully met.

Table 3.1 Machine capacities and resource consumption for Case Study 1

Machine M1 M2 M3

Capacity [h] 120 140 110

Consumption U-A [h/bike] 0.2 0.3 0.5

Consumption U-B [h/bike] 0.4 0.2 0.5

Table 3.2 Demand quantity and CMs for Case Study 1

Product U-A U-B

Demand [Bicycles]

CM [EUR/bike]

300 200

110 140

3.1 Declaration and initialization of model parameters

3.1

25

Declaration and initialization of model parameters

Model parameters (a.k.a. data elements, input parameters, parameters, input data, or data) are those elements of a model representing fixed values that the user has to specify in advance, such as contribution margins, resource capacities, resource consumption, or others. To make changing these values easy in an already formulated model without individually changing every single line of the model, model parameters are usually included in the form of placeholders. The declaration and initialization of placeholders with specific values are carried out in one go at the beginning of the model file. The placeholders are then utilized in the later components of the model, such as the objective function and the constraints (cf. Sections 3.3 and 3.4). This way of modeling is also called modularization. When included as placeholders, model parameters are denoted by unique identifiers. Identifiers must not contain spaces, special characters, or umlauts but can contain both upper- and lowercase letters. Please note that identifiers are case-sensitive: Therefore, an identifier "capacity" is not identical to the identifier "Capacity." 3.1.1 Declaration Parameters have to be declared before they can be used, which means a data type is assigned to them. The available elementary data types are shown in Table 3.3: integers, real numbers, and strings. The data type is defined by means of the corresponding keyword, which must be placed in front of the parameter. Table 3.3 Elementary data types for model parameters

Keyword

Description

int float

Integer Real number

string

String

Integers and real numbers (floating point numbers) are declared using the keywords int and float, respectively. They can take on both positive and negative values. Strings are declared with the keyword string. 3.1.2 Initialization In addition to the declaration, the user also has to initialize model parameters (i.e., assign a specific value with the operator "="). The declaration and initialization of the model parameters can be done in one line. The general syntax is as follows: Data type identifier = value;

26

3 Setting up a model

Example 3.1 shows the declaration and initialization of some integer model parameters.

Example 3.1 1 2 3 4 5

int int int int int

demand1 = 300; demand2 = 200; cap1 = 120; cap2 = 140; cap3 = 110;

//demand for bike type 1 (U-A) //demand for bike type 2 (U-B) //total machine time of M1 in the planning period //total machine time of M2 in the planning period //total machine time of M3 in the planning period

Example 3.1 also demonstrates the usage of comments in the program code: All characters in a line starting with the string "//" are ignored by the solver. This allows us to insert comments without destroying the OPL syntax. Comments over multiple lines can be realized using the opening string "/*" in combination with the closing string "*/". Note that in Example 3.1, as well as in all other program code examples, the line number is given at the beginning of each line for better readability and referencing. The line numbers are not part of the program code and, therefore, must not be entered into an OPL model. When initializing integer parameters, the largest possible integer to be implemented in OPL can be generated using the keyword maxint (= (2 − 1) for 64-bit operating systems), as Example 3.2 illustrates. An analogous keyword for the smallest integer does not exist, but -maxint can be used for this purpose.

Example 3.2 1 int demand3 = maxint;

//a product with very high demand

When initializing model parameters with real numbers, a dot is used to separate the decimal places, as shown by the declaration and initialization in Example 3.3.

Example 3.3 1 2 3 4 5 6 7 8

float float float float float float float float

consumption11 consumption12 consumption13 consumption21 consumption22 consumption23 cm1 = 110; cm2 = 140;

= = = = = =

0.2; 0.3; 0.5; 0.4; 0.2; 0.5;

//resource consumption //resource consumption //resource consumption //resource consumption //resource consumption //resource consumption //CM of bicycle type 1 //CM of bicycle type 2

of of of of of of

type type type type type type

1 1 1 2 2 2

on on on on on on

M1 M2 M3 M1 M2 M3

In OPL, there is also a predefined constant infinity that can be used for parameters of the data type float (cf. Example 3.4).

3.1 Declaration and initialization of model parameters

27

Example 3.4 1 //one type of bicycle with the largest possible negative CM 2 float cm3 = -infinity; //=> no production

Instead of immediately assigning a value, the initialization of integers and real numbers can also be done by using an arithmetic operation. The basic operators can be found in Table 3.4. Within the operations, model parameters already initialized in previous statements can be used (cf. Example 3.5, lines 2 and 5). Table 3.4 Basic arithmetic operators

Operation x+y x-y x*y x/y x div y x%y or x mod y minl(x1,...,xn) maxl(x1,...,xn) abs(x) ln (x) exp (x)

Description Addition Subtraction Multiplication Division Integer division (without remainder) Remainder for integer division Minimum Maximum Absolute value Natural logarithm (Natural) Exponential function

Example 3.5 1 2 3 4 5 6

int demand1 = 300; int demand2 = demand1 float consumption11 = float consumption12 = float consumption13 =

//demand for type 1 - 100; //demand for type 2 as function of type 1 0.2; //resource consumption of type 1 on M1 0.3; //resource consumption of type 1 on M2 consumption11 + consumption12; //resource consumption of type 1 on M3

When initializing strings, the assigned string must be enclosed in quotation marks (""). Further, some characters within the string must be entered via escape sequences, analogous to other programming languages to distinguish them from the rest of the program code. We give important examples of escape sequences in Table 3.5. To initialize a string that spans multiple lines of program code, line breaks must be indicated by using a backslash (\). Example 3.6 shows the most common use cases of escape sequences.

28

3 Setting up a model

Table 3.5 Important escape sequences within strings

Escape sequence \t \n \" \\

Description Tab New line Quotation marks Backslash (\)

Example 3.6 1 2 3 4

string textModule = "The demand for bicycle type \"U-A\" is:"; //is identical to string textModule2 = "The demand for \ bicycle type \"U-A\" is:";

The declaration of parameters can only take place in the model file using OPL. By contrast, the initialization can also occur in an IBM ILOG Script block (cf. Chapter 5). If the same model is to be solved with several alternative parameter configurations or if projects are somewhat more extensive, it is advantageous to manage the actual values of the model parameters in a separate data file (file extension .dat). With this separation of model and data, the initialization is only done by assigning the placeholder "...", whereby from the corresponding data file the specific values are automatically integrated into the context of the solution process (cf. Chapter 7). In addition to the elementary data types discussed in this chapter, there are other composite data types. We discuss these in Chapters 4 and 6.

3.2

Declaration of decision variables

In the second component of the model file, the decision variables of the model are usually declared. The decision variables are the levers of the optimization model—that is, their values are determined automatically during optimization to best achieve the desired objective. More precisely, unlike the model parameters, the values of decision variables are not determined by the user but by CPLEX during the solution process. The resulting values of the decision variables can be observed in the Problem browser of the Studio after the optimization has been performed (cf. Section 2.3.4, Fig. 2.8). Like parameters, decision variables are denoted by unique identifiers. As with the parameters, these are case-sensitive and must not contain spaces, special characters, or umlauts. Analogous to parameters, decision variables must be declared before they can be used. The following elementary data types are available for this purpose: integers (int), real numbers (float), and a binary data type (boolean) that may only take the values 0 or 1

3.2 Declaration of decision variables

29

(cf. Table 3.6). The data type is specified using the corresponding keyword, followed by the variable’s identifier. In addition, the keyword dvar must be put in front to distinguish decision variables from model parameters. Table 3.6 Elementary data types for decision variables

Keyword

Description

int int+

Integer Non-negative integer

float float+

Real number Non-negative real number

boolean

Binary (value 0 or 1)

The declaration of integer and real-valued decision variables precisely follows the explanations in Section 3.1 for model parameters (cf. Example 3.7).

Example 3.7 1 dvar int quantity1; //planned produced units of bike type 1 2 dvar int quantity2; //planned produced units of bike type 2

For decision variables, it is also possible to further restrict the domain of the variable in advance. We do this by using the keyword in, followed by the specification of the (closed) interval in which the values may lie. Upper and lower limits are to be separated by ".." (cf. Example 3.8, lines 2 and 8).

Example 3.8 1 int demand1 = 300; //demand for bike type 1 (parameter) 2 dvar int quantity1 in 0..demand1; //the production quantity must be 3 // less than or equal to the demand 4 dvar float inputQuantity in 2.2..3.2;

As a shortcut to exclude negative numbers, it is also possible to use int+ and float+ as keywords, as Example 3.9 illustrates.

Example 3.9 1 2 3 4 5 6 7

dvar float+ production; //is identical to dvar float production2 in 0..infinity; dvar int+ production3; //is identical to dvar int production4 in 0..maxint;

30

3 Setting up a model

The keyword boolean is used for the declaration of binary decision variables. Ultimately, this is a shorthand notation of an integer variable whose domain is restricted to the values 0 and 1 (cf. Example 3.10).

Example 3.10 1 dvar boolean decision; 2 //is identical to 3 dvar int decision2 in 0..1;

3.3

Objective function

The objective function specifies the objective of the optimization model in the form of a mathematical function depending on one or more decision variables. To formulate the function, decision variables and model parameters can be linked by arithmetic operators. We give an overview of the basic operators in Table 3.4 (Section 3.1.2). The solution methods available in CPLEX are predominantly designed for linear models. Therefore, when modeling the objective function, users must take care to ensure it is formulated linearly with respect to the decision variables. For instance, given two decision variables var1, var2, and the model parameter param, the expression var1*param is allowed. By contrast, the product of multiple decision variables var1*var2 or the exponential function exp(var1) would result in a nonlinear objective function. However, certain nonlinear operators are also valid in the context of decision variables, insofar as they can be linearized. The linearization is performed internally by CPLEX and, as part of the optimization, is invisible to the user. This applies, e.g., to the operators abs, minl, and maxl (cf. Table 3.4 in Sections 3.1.2 and 4.4.2). Depending on the problem to solve, the objective function is to be minimized or maximized, as specified by the preceding keyword minimize or maximize. Once the solution process has started, CPLEX determines the values of the decision variables in such a way that the objective function becomes the smallest or largest possible, according to the direction of the optimization. In the context of Case Study 1, Example 3.11 shows the definition of a maximization objective function with decision variables quantity1 and quantity2 and associated coefficients cm1 and cm2.

Example 3.11 1 2 3 4 5

include "example_3.01.mod"; include "example_3.03.mod"; include "example_3.07.mod"; maximize cm1*quantity1 + cm2*quantity2; //maximize Total CM

3.3 Objective function

Tip 6

31

Studio output of an unbounded model

Example 3.11 is an unbounded model. This means it is not constrained in the direction of optimization (i.e., the objective function value goes to infinity). The Problem browser displays the somewhat misleading output "Solution with objective 0E0." The Solutions and Statistics tabs are somewhat more helpful as they indicate an unbounded solution: "solution (unbounded) with objective 0." Tip 7

The keyword "include"

The keyword include can be used to integrate other, already existing model files into the current model file. In the first three lines of Example 3.11, this is done with three model files located in the same project folder as the current model file. The path for a model file to be included can always be specified relative to the location of the current file (as in the example) or in an absolute way (examples of this can be found in Section 10.2). If objective functions are very complex but consist of several logically distinguishable components, it can, for the sake of clarity, be helpful to separate the function into individual terms. Such terms—called decision expressions— dexprare defined by the keyword dexpr. The definition is usually located in the model file between the declaration of the decision variables and the definition of the objective function. Terms are assigned to named decision expressions by using the operator "=". They are formed arithmetically from parameters and decision variables (cf. Example 3.12, lines 5 and 6).

Example 3.12 1 2 3 4 5 6 7 8

include "example_3.01.mod"; include "example_3.03.mod"; include "example_3.07.mod"; dexpr float tcm1 = cm1*quantity1; //Total CM of bike type 1 dexpr float tcm2 = cm2*quantity2; //Total CM of bike type 1 maximize tcm1 + tcm2;

//maximize Total CM

Finally, users should note that a model and, consequently, a model file does not necessarily have to contain an objective function. This is the case if no optimization problem but only a feasibility problem has to be solved. This means that, given the constraints (cf. Section 3.4), only an arbitrary feasible solution has to be determined—that is, a combination of values for the decision variables so that all constraints are met.

32

3.4

3 Setting up a model

Constraints

Constraints specify the restrictions to be met by a feasible solution of the model. The constraints are described within a subject-to block. All constraints must be enclosed in curly brackets {} after the keyword subject to. Instead of the keyword subject to, the keyword constraints can also be used. Table 3.7 Relational operators for constraints

Operator x==y x!=y x=y

Description Equal to Unequal to Less than or equal to Greater than or equal to

From a mathematical point of view, the most common forms of constraints are equations and inequalities. They are formulated with relational operators, which are shown in Table 3.7. As in the objective function, we use the arithmetic operators given in Table 3.4 to form terms with the help of decision variables and model parameters, which are then linked to form the equation or inequality using the corresponding relational operator. Example 3.13 shows their application in the context of Case Study 1.

Example 3.13 1 include "example_3.11.mod"; 2 3 subject to { 4 consumption11*quantity1 + consumption21*quantity2 = p.demand; //Constraints (2): Compliance with production capacities forall (a in plants) sum(p in products) productionQuantity[p][a] = 0 }; //Decision variables dvar int+ productionQuantity[productionOptions];

106

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

6 Modeling with tuples

//Objective function minimize sum(opt in productionOptions) productionQuantity[opt]*opt.cost; //Constraints subject to { //Constraints (1): Meeting demand forall (p in products) sum( in productionOptions) productionQuantity[] >= p.demand; //Constraints (2): Compliance with production capacities forall (a in plants) sum( in productionOptions) productionQuantity[] = p.demand; //Constraints (2): Compliance with production capacities forall (a in plants) sum( in productionOptions) productionQuantity[] = 1000}; {Product} s2 = {p | p in products: (p.weight/p.nUnits) > 500}; {Product} s3 = {p | p in products: (p.packaging.emptyWeight > 100) || (p.nUnits > 1)}; {Product} s4 = {p | p in products: (p.packaging.emptyWeight + p.weight >= 1500) && (p.nUnits == 1)}; execute { for (var v in s1) { writeln(v.packaging.emptyWeight); } writeln(s2); for (var v in s3) { writeln(v.name + " in " + v.packaging.name); } for (var v in s4) { writeln(v.packaging); } }

9. Consider the following model. Why is this an example of inefficient modeling? Make the model more efficient. 1 2 3 4 5 6 7 8 9 10

tuple Product { key string name; int lotSize; } tuple Machine { key string name; int productionTime; }

6.5 Exercises

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

111

{Product} products = {, , }; {Machine} machines = {, , , }; tuple ProductionProcess{ string product; int lotSize; string machine; int productionTime; } {ProductionProcess} productionProcesses = { , , , , };

10. Use the corrected (i.e., more efficient) model from the previous exercise as a basis. Transform the following modeling with explicit slicing into a modeling with implicit slicing. 1 //Explicit slicing 2 int throughputTimeExplicit[product in products] = 3 sum( in productionProcesses: product == p) 4 m.productionTime*p.lotSize;

7

Separating model and data

The previous chapters mostly included model parameters in the form of placeholders that were declared and then initialized with specific values at the beginning of the respective model file. Thus, a model can be formulated generically and solved with different input data without modifying each line of the model individually (cf. modularization, Section 3.1). In larger examples, and especially in real-world applications, it is often helpful to separate model and data even more clearly. In fact, it is possible to solve a model with data from different data sources. The optimization model developer can then test the model— e.g., first with small data sets containing only a few records defined in a text file and, subsequently, with more records in an Excel file. This chapter describes the different possible ways of reading in data. The following case study serves as an illustration. The complete solution of the case study is given in Section 7.4.

RideEasy Case Study 5: Simulation of production scenarios RideEasy’s production manager has to plan the production capacities that should be maintained in the plants around the world for the next few years. Since the available forecasts on how the RideEasy products’ sales could develop are quite vague, he wants to evaluate possible plans regarding different future scenarios. His analytics department has developed an OPL optimization model for him. The optimization model calculates an optimal production allocation for the plants based on forecast orders, which minimizes the sum of production costs and transportation costs. It also determines which production costs are actually incurred and whether all orders can actually be fulfilled. There are still five manufacturing zones: North America (NA), South America (SA), Europe (EU), Africa (AF), and Asia (AS). However, the plants in North and South © Springer-Verlag GmbH Germany, part of Springer Nature 2022 S. Nickel et al., Decision Optimization with IBM ILOG CPLEX Optimization Studio, Graduate Texts in Operations Research, https://doi.org/10.1007/978-3-662-65481-1_7

113

114

7 Seperating model and data

America, as well as Africa, are to be closed temporarily, so planning will only be done for the two plants in Europe and Asia. For the present planning and optimization, the models do not distinguish between different products (aggregated approach). For each of the plants, a maximum production capacity and production cost rate are given: the European plant can produce 1,000 units at EUR 500 each, and the Asia plant 100 units at EUR 400 each (cf. Table 7.1). The transportation costs result from the estimated freight costs. For this purpose, the price table of a forwarding company, which shows corresponding unit prices for the two regions, is available (cf. Table 7.2). For each region, we have the historical order quantities of the past 10 years. The corresponding values of the previous year are given in Table 7.3. The order quantities should be multiplied by a forecast value for future planning. This is assumed to be 80%, 90%, 100%, 110%, and 120% (for different future scenarios). For now, the production manager does not need the model to be completely adapted for all future scenarios in each case. Rather, he wants the model to be modified so that he can simply have it solved with different input data. For this purpose, a separation into model file and data file is to be made. The different input data could even come from different technical systems—e.g., in the form of text files or Excel files.

Table 7.1 Data for EU and AS plants

Region EU AS

Name Europe plant Asia plant

Capacity 1,000 100

Production cost rate (EUR) 500 400

Table 7.2 Freight costs

Freight costs from/to EU AS

NA 30 50

SA 40 50

EU 20 60

Table 7.3 Historical order quantities

Regions NA SA EU AF AS

Historical order quantities 200 100 300 50 200

AF 50 60

AS 50 20

7.1 Internal and external data initialization

7.1

115

Internal and external data initialization

In all previous examples, the data elements (i.e., model parameters) were initialized within the model file. This is referred to as internal data initialization. Alternatively, OPL permits external data initialization outside the model file. This allows the separation of model and data. Example 7.1.mod illustrates the two types of initialization. nRegionsWest is initialized internally by assigning the value 3. nRegionsEast is initialized externally, i.e., not within the model file. nRegions is again initialized internally based on the preceding initializations. As can be seen in nRegionsEast, the external data initialization is indicated by the ellipsis "..." (cf. Table 7.4). This means that the data element is declared here, but it is not yet given a value. The value is added at runtime from another data source. The resulting model is independent of the actual data and can be solved with different data sets and data sources. Table 7.4 OPL language element for external data initialization

Keyword

Description

...

Ellipsis, indicating that a data element is initialized externally. Three dots ("...") are written for this purpose.

Example 7.1.mod 1 int nRegionsWest = 3; //internal initialization 2 int nRegionsEast = ...; //external initialization 3 int nRegions = nRegionsWest + nRegionsEast;

A model file in which (at least) one data element is only declared but not initialized cannot be executed on its own. For example, if one tries to run Example 7.1.mod in the Studio in a run configuration, the error message "The data element nRegionsEast is not initialized" will be generated. The simplest way to fill an external data element with specific values is to use a data file (cf. Section 2.3.2). This is a separate (text) file whose content must follow a specific syntax of its own, and whose name ends with ".dat". Example 7.1.dat shows a data file that matches Example 7.1.mod.

Example 7.1.dat 1 nRegionsEast = 2;

nRegionsEast is explicitly initialized in the data file. Alternatively, data originating from other applications can be read into a data file, as will be shown in Section 7.3.

116

7 Seperating model and data

The model from Example 7.1.mod can now be executed in the Studio together with the data file from Example 7.1.dat. For this purpose (as already explained in Section 2.3.2), the model and the data file must be combined in a run configuration (cf. Fig. 7.1).

Figure 7.1 Example project in the Studio with a run configuration for running a model with a data file

Often there are several data files for one model. Thus, the model is executed independently with one data file after the other—e.g., to test different data sets or data sources. However, a model can also be executed simultaneously with several data files if initializing the model’s data elements is distributed over several data files. In this case, these data files have to be disjoint (i.e., each data element to be initialized can only be initialized in exactly one data file). If, as in Example 7.1.mod, some data elements in a model are initialized internally and others externally, the initialization order of OPL plays a decisive role: 1. First, all external initializations are performed. 2. Then, any existing execute blocks are processed in the order in which they appear in the model (eventually initializing further data). 3. Internal initializations are performed only at the moment when the value of the corresponding data element is used for the first time—e.g., in an execute block or in a subsequent internal initialization. The procedure in point 3 is called lazy initialization, which can be interpreted as "late initialization." The basic idea is that data elements will be created only when they are needed. However, because of the resulting initialization sequence, a simple and seemingly harmless statement can have peculiar side effects that can affect both runtime performance and the content of the data elements and, thus, the optimization results. On the one hand, a simple writeln(all) can take an unexpectedly long time if the data element all has not been used before. In such a case, it has to be created for the first time for this writeln() statement, which can take a long time, depending on the content of all. On the other hand, there can be semantics-related effects that the modeler did not intend, which can lead to model errors, as Example 7.2 and Example 7.3 with the corresponding outputs illustrate.

7.1 Internal and external data initialization

117

In Example 7.2.mod, nRegions is initialized with the expression nRegions = 0 + 2. This is done implicitly as part of the first use in the first writeln() statement in the execute block (line 7). The external initialization is done with Example 7.2.dat.

Example 7.2.mod 1 2 3 4 5 6 7 8 9 10 11 12

int nRegionsWest;

//this element is not initialized and // gets the default value 0 int nRegionsEast = ...; //external initialization int nRegions = nRegionsWest + nRegionsEast; execute { writeln(nRegionsWest, " ", nRegionsEast, " ", nRegions); nRegionsWest = 3; writeln(nRegionsWest, " ", nRegionsEast, " ", nRegions); nRegionsEast = 6; writeln(nRegionsWest, " ", nRegionsEast, " ", nRegions); }

Example 7.2.dat 1 nRegionsEast = 2;

Output of the run configuration with the files Example 7.2.mod and Example 7.2.dat in the Scripting log tab: 0 2 2 3 2 2 3 6 2 The situation is different in Example 7.3.mod, which differs from Example 7.2.mod only in that the corresponding writeln() statement has been commented out there. Contrary to expectations, this now also has a direct effect on the calculation of nRegions, of which the (final) value is now 5 instead of 2. The initialization is now implicitly done with the next writeln() statement (line 9) by nRegions = 3 + 2, after nRegionsWest has meanwhile been set to 3.

Example 7.3.mod 1 2 3 4 5 6 7 8 9 10 11 12

int nRegionsWest;

//this element is not initialized and // gets the default value 0 int nRegionsEast = ...; //external initialization int nRegions = nRegionsWest + nRegionsEast; execute { //writeln(nRegionsWest, " ", nRegionsEast, " ", nRegions); nRegionsWest = 3; writeln(nRegionsWest, " ", nRegionsEast, " ", nRegions); nRegionsEast = 6; writeln(nRegionsWest, " ", nRegionsEast, " ", nRegions); }

118

7 Seperating model and data

Example 7.3.dat 1 nRegionsEast = 2;

Output of the run configuration with the files Example 7.3.mod and Example 7.3.dat in the Scripting log tab: 3 2 5 3 6 5 Further examples and explanations of the initialization sequence and lazy initialization can be found in the Studio Help. To give the full picture, we should mention that data files are only one way of initializing data elements externally. In addition, it is possible to integrate an OPL model into own applications (e.g., a Python program) and to fill it with data from the embedding application at runtime. For example, in a productive planning system, the required data can be loaded from a database, and after optimization, the results can be written back to the database. However, this requires programming in a programming language such as Python, Java, or C++. Executing an OPL model in an application essentially involves the following steps: 1. Read in the OPL model 2. Fill the OPL data elements 3. Execute the OPL model including optimization 4. Process the optimization results IBM ILOG CPLEX Optimization Studio provides Application Programming Interfaces (APIs) in various programming languages, such as Python, C++, Java, and .Net. For details, please refer to the Studio Help.

7.2

Explicit initialization in data files

Data files allow explicit external initialization for a model. The data is entered manually as text in the data file. Regarding the content and structure of data and model files, there are some similarities but also many differences. Table 7.5 gives a summarized comparison. In the following subsections, we then go into more detail about the explicit initialization of the different data types.

7.2 Explicit initialization in data files

119

Table 7.5 Comparison of model files and data files

Model file (.mod)

Data file (.dat)

File name

Ends in .mod (e.g., problem.mod).

Ends in .dat (e.g., problem.dat).

Declaration

Yes. All data elements are declared in the model.

No. A declaration of data elements is not allowed in the data file.

Initialization

Yes. Data elements can be initialized in the model (internal initialization). Example:

Yes. Data elements can be initialized in the data file (external initialization). Example:

int i = 1; Note: Separation of declaration and initialization is not allowed. (Exception: initialization with IBM ILOG Script. Example: int i; i = 1;)

Calculations

Yes. In the model itself, data elements can be generated by calculation. Example: int int a + int [i:

i = 1; Note: If a data element has already been initialized in the model, it must not be set a second time in the data file. Each data element initialized in a data file must have been previously declared in a model file and marked as needing to be initialized externally with an ellipsis (...). No. Calculations or similar (e.g., set operations) are not allowed in data files.

a = 1; b[1..5] = [a + 1, a + 2, 3, a + 4, a + 5]; c[1..5] = a + i | i in 1..5];

Further examples can be found in Chapter 4, Example 4.8, Example 4.10, and Example 4.11. IBM ILOG Script

Yes. In execute and main blocks (cf. Chapter 5, Chapter 9, and Chapter 10).

Limited. execute blocks are not allowed in data files. For special use cases, there is a prepare block for IBM ILOG Script statements. This is not discussed in detail in this book.

External data sources

Limited. Data from external data sources cannot be processed directly in model files. Exception: integration via IBM ILOG Script (cf. Section 9.1).

Yes, special commands for processing data from Excel can be used in data files (cf. Section 7.3).

120

7 Seperating model and data

Table 7.5 (continued)

Ranges

Yes, each range is initialized in the model file.

No. Ranges cannot be initialized in data files. However, with the following procedure (in a model file), at least the limits of a range can be initialized externally: int n = ...; range r = 1..n;

Syntax: Strings

Strings must always be written with en- In data files, strings that do not conclosing quotation marks in model files. tain special characters, such as spaces, can also be written without quotation Example: marks. Example: string text = "world"; text = world;

Syntax: List of values (for array or set)

A list of values to initialize an array or set must always be separated by commas. Example:

In data files, the commas can be omitted. Example: arr = [1 2];

int arr[1..2] = [1,2];

No. The use of value pairs to initialize Syntax: Value pairs for arrays ("#" syntax) is not allowed in model files. arrays

Yes. Arrays can be initialized with value pairs in data files. Example:

No. The use of value pairs to initialize Syntax: Value pairs for tuples ("#" syntax) is not allowed in model files. tuples

Yes. Tuples can be initialized in data files with pairs of values. Example:

arr = #[1:4 2:17]#;

edge = ##;

7.2.1 Basic model parameters The model parameters of basic data types already discussed in Section 3.1.1, which store a single value and not a data structure consisting of several values, are initialized in data files according to the syntax for an internal initialization. Since the declaration is made in the model file, the data type need not be specified in this case. Example 7.4.mod and Example 7.4.dat illustrate the implementation.

Example 7.4.mod 1 float forecast = ...; 2 string region = ...;

7.2 Explicit initialization in data files

121

Example 7.4.dat 1 forecast = 1.1; 2 region = "EU";

7.2.2 Arrays In the previous chapters, several examples showed how arrays can be initialized (internally) in model files. For the (external) initialization of arrays in data files, additional constructs are available, as illustrated in Example 7.5.mod and Example 7.5.dat.

Example 7.5.mod 1 2 3 4 5 6

int nRegions = ...; //Attention: a range can only be initialized internally! range regions = 1..nRegions; string name[regions] = ...; string description[regions] = ...; float freightCosts[regions][regions] = ...;

Example 7.5.dat 1 2 3 4 5 6 7

nRegions = 5; name = [NA SA EU AF AS]; description = #[ 3:"Europe", 1:"North America", 2:"South America", 5:"Asia", 4:"Africa" ]#; freightCosts = #[3:[30, 40, 20, 50, 50], 5:[50, 50, 60, 60, 20]]#;

The initialization of the array name (line 2) correlates with the syntax of a corresponding internal initialization. However, we need to note that the elements of the array, despite being of the type string, do not have to be enclosed in quotation marks—i.e., if they consist only of letters without umlauts or special characters. Furthermore, the list’s values do not have to be separated by commas. The array description is initialized by specifying pairs of values with one pair per entry: index:value (lines 3 to 6). This allows the array’s entries to be specified in any order. Note that, for this notation, the array must be enclosed with #[]#—i.e., by additionally preceding and appending the hash character "#". Again, using separating commas between entries is optional, and the quotation marks can be omitted if necessary. So, alternatively, one could define the array as follows: #[3:Europe 1:"North America" 2:"South America" 5:Asia 4:Africa]#. The two-dimensional array freightCosts can also be initialized with value pairs, as shown in Example 7.5.dat (line 7). This example specifies only required values for the plant locations that actually have to be considered. If indices are not listed during initialization, the array’s corresponding element is automatically initialized with the value 0—in this

122

7 Seperating model and data

case, for the regions of origin North America, South America, and Africa. This must be taken into account when the array is used later in the model. 7.2.3 Sets A set is specified—similarly to the syntax for internal initialization—in a data file by using a simple list of elements, enclosed in curly brackets (cf. the set regions in Example 7.6.mod and Example 7.6.dat).

Example 7.6.mod 1 {string} regions = ...;

Example 7.6.dat 1 regions = {"NA", "SA", "EU", "AF", "AS"};

Note that in data files for sets (and for basic elements), no calculations are allowed. Therefore, it is not possible to use set operators (such as union or inter) here. Such parameter initializations have to be done in the model file. At the same time, similarly to the arrays, the list of elements that make up a set can be written without separating commas, and for string elements the quotation marks can be omitted. 7.2.4 Tuples Tuples can be initialized in a data file in the same way as in a model file. Alternatively, initialization by value pairs, triples, etc. is possible. Each tuple’s initialization must then begin and end with a hash character, deviating from the array syntax, where the entire set is enclosed with hash characters. Example 7.7.mod with Example 7.7.dat shows the two alternative notations for the tuple set transportRoutes. Individual tuples can also be initialized with this notation (a tuple routeAB of type TransportRoute): routeAB = ##;

Example 7.7.mod 1 tuple TransportRoute { 2 string origin; 3 string destination; 4 float cost; 5} 6 {TransportRoute} transportRoutes = ...;

Example 7.7.dat 1 transportRoutes = 2 {, , , , , 3 , , , , };

7.3 Connecting spreadsheet files

123

4 /* Alternative syntax: 5 transportRoutes = 6 {##, 7 ##, 8 ##, 9 ##, 10 ##, 11 ##, 12 ##, 13 ##, 14 ##, 15 ##}; 16 */

7.3

Connecting spreadsheet files

In addition to explicit external initialization, data files can also be used as a bridge to other external data sources. For example, CPLEX Optimization Studio offers the possibility of connecting Microsoft Excel spreadsheet files. Table 7.6 gives an overview of the related OPL language elements. In Example 7.8.mod, the data elements nRegions, name, description, transportRoutes, distance, and freightCosts are declared. In contrast to Example 7.7.mod, the tuple TransportRoute no longer contains a cost attribute, since the (freight) costs in this example are to be read in via the two-dimensional array freightCosts. These data elements are initialized via the data file Example 7.8.dat with values from an Excel file. The data is loaded from the Excel file "OplBookExample7.8.xlsx" in lines 1 to 8 using the commands SheetConnection and SheetRead. Fig. 7.2 shows the worksheet "Sheet1" of the Excel file with the file name "OplBookExample7.8.xlsx", created with Microsoft Excel, and then imported into the current OPL project. Table 7.6 OPL language elements for accessing Microsoft Excel files

Keyword SheetConnection xyz("c:/path/file.xlsx") from SheetRead(xyz,"range")

to SheetWrite(xyz,"range")

Description Opens and names a new connection to an existing Excel file. xyz is the (arbitrary) name for the new connection. Reads OPL data over the existing connection xyz from the file file.xslx from the cell range range. Writes OPL data over the existing connection xyz to the file file.xslx in the cell range range.

124

7 Seperating model and data

Example 7.8.mod 1 2 3 4 5 6 7 8 9 10 11 12 13 14

int nRegions = ...; range regions= 1..nRegions; string name[regions] = ...; string description[regions] = ...; tuple TransportRoute { int origin; int destination; } {TransportRoute} transportRoutes = ...; int distance[transportRoutes] = ...; int freightCosts[regions][regions] = ...;

With SheetConnection, a connection to the Excel file "OplBookExample7.8.xlsx" is established (line 1). If the file has not been imported into the project or is located in a sub-

Figure 7.2 Extract from the Excel file "OplBookExample7.8.xlsx" (worksheet "Sheet1"), which can be used to initialize the data elements from Example 7.8.mod (cf. the corresponding data file Example 7.8.dat).

7.3 Connecting spreadsheet files

125

folder, the file’s directory path must also be specified. The connection to the file is given the arbitrarily selected name xlsx. This name is used to reference the connection in further commands. If other Excel files were connected, a different name would have to be selected for the corresponding connections. SheetRead is then used to read data via the named Excel connection (here: xlsx) from a cell range of a spreadsheet into the corresponding predefined data element (lines 3 to 8). The worksheet and cell range are specified in standard Excel notation. Thus, "'Sheet1'!C2:C6" denotes the cell range "C2:C6"—i.e., the five cells C2 to C6 of the worksheet "Sheet1."

Example 7.8.dat 1 2 3 4 5 6 7 8 9 10

SheetConnection xlsx("OplBookExample7.8.xlsx"); nRegions from SheetRead(xlsx, "'Sheet1'!A2"); name from SheetRead(xlsx, "'Sheet1'!C2:C6"); description from SheetRead(xlsx, "'Sheet1'!E2:E6"); transportRoutes from SheetRead(xlsx, "'Sheet1'!A11:B20"); distance from SheetRead(xlsx, "'Sheet1'!E11:E20"); freightCosts from SheetRead(xlsx, "'Sheet1'!B25:F29"); name to SheetWrite(xlsx, "'Sheet1'!G11:G15");

For the exact definition of such cell range specifications, please refer to the Microsoft Excel Help. By using SheetRead several times, the following data elements are initialized in Example 7.8.dat: ∂ nRegions: A scalar model parameter initialized from a single cell ("A2") in the spreadsheet. ∂ name: A one-dimensional array that will contain five elements according to the index set (1..nRegions) is initialized with the cell range "C2:C6"—i.e., the cells C2, C3, C4, C5, and C6 in column C, which are located below each other. Alternatively, it is possible to select the corresponding cell range from an Excel table row (i.e., to arrange the elements in a horizontal table). ∂ description: A one-dimensional array initialized in exactly the same way as name from five Excel cells ("E2:E6"). ∂ transportRoutes: A set of tuples that is read row by row from a table. The table consists of two columns because the underlying tuple type TransportRoute contains two attributes. A total of 10 tuples are read in, which are located in the cell range "A11:B20". ∂ distance: A one-dimensional array that uses transportRoutes as the index set. For each route (here: 10 routes), a value is read from a cell of the cell range "E11:E20".

126

7 Seperating model and data

∂ freightCosts: A two-dimensional array read from a matrix ("B25:F29"). The first dimension of the array (index set regions) refers to the horizontal axis— i.e., the rows of the matrix; the second dimension (also index set regions) refers to the vertical axis. The Excel cells without an entry lead to an initialization of the corresponding entry of the OPL array with 0. The SheetWrite command can be used to write data from OPL to the Excel file. This is typically optimization results, but any data—even original input data—can be written. When Example 7.8.mod is run in conjunction with Example 7.8.dat and the Excel file shown in Fig. 7.2, OPL writes the content of the name array to the cell range "'Sheet1'!G11:G15" (row 10 in Example 7.8.dat) at the end of the execution. This is empty in Fig. 7.2 but would then be filled appropriately after execution. It is important to note that data can only be written to an Excel file if it is not open in the Excel application at the same time; otherwise, OPL will report an access error. Example 7.8.dat contains instructions for communicating with an Excel file and for reading and writing data from and into this file. As indicated above, in such a data file it is also possible to work with several Excel files, which are connected via several SheetConnection commands. In addition, such a data file can also contain all instructions for directly initializing data (from Section 7.1)—therefore, the data initialization with Excel can coincide with the standard initializations without further ado. Tip 13

Named cell ranges in Excel

Excel data must be referenced—as described—in the commands SheetRead and SheetWrite via Excel cell range specifications (e.g., "'Sheet1'!D3:D5"). The size of these cell range specifications must exactly match the declared "size" of the data. In particular, the cell range must not be specified larger than the actual data in the Excel file. Alternatively, "named cell ranges" can be used. Details on how to set up such cell ranges using the Excel Name Manager can be found in the Microsoft Excel Help. The user can use it to assign the name "transportRoutes" to a section of a spreadsheet in Excel, for instance, as in the example, to the cell range "A11:C20". This named cell range can then be referenced in the corresponding commands in the data file in the following way: transportRoutes from SheetRead(xlsx, "transportRoutes"); The advantage of this approach is that the named cell range "transportRoutes" can then be resized in the Excel file, if necessary, without having to make changes to the associated data file. (Cf. also the corresponding examples in Section 7.4.) In the Excel file to be processed, graphical formatting—such as using colors (cf. the gray background in tables and matrices, Fig. 7.2), frames, fonts, etc.—plays no role. The data format, by contrast, is taken into account when elements of the type string are read.

7.4 Solution of Case Study 5

127

Thus, a date will be transferred according to the format the user configured for the corresponding Excel cell (e.g., as "01.01.2022"). The data cells in the Excel file can also contain formulas. In this case, the value last calculated for the formula—and saved in the file—is always read in. Readers can find more information, details, and examples on how to integrate Excel files in the Studio Help. In addition to connecting spreadsheet files, IBM ILOG Script can also be used to read in data from arbitrarily structured text files, such as CSV files. This is covered in the advanced applications of IBM ILOG Script in Section 9.2. Furthermore, the latest version of the CPLEX Optimization Studio also (again) supports connections to databases (not covered in the book).

7.4

Solution of Case Study 5

Here at the end of the chapter, we present a solution to the case study to show how the corresponding model can be filled either explicitly from the data file or implicitly from an Excel file. In Example 7.9.mod, data elements of several data types—most of which are already known from the previous examples—are defined and initialized externally in each case. The tuple set regions lists all regions of the planning problem. The matrix freightCosts contains a cost value of type float for each possible pair of regions— i.e., for two regions a and b, freightCosts[a][b] describes the freight costs from a to b. The tuple set orders contains a data set of historical orders for all regions. Note that here these orders do not refer to a product, because the present planning does not consider individual products but all products in an aggregated way. Moreover, these orders do not refer to a point in time because the planning also abstracts from specific points in time and implicitly refers to an overall period—e.g., one year. The tuple set plants contains all industrial plants. For each plant, the geographical region of the plant location, a production capacity (in units, independent of the product), and the production costs (per unit, independent of the product) are given. The (scalar) factor forecast describes the expected quantity of future orders: E.g., the value 1.1 would mean an increase of 10 %; the value 0.95 a decrease of 5 %. In the model, this factor is multiplied with the values of the historical orders and, thus, results in the expected future order quantity (for the planning period).

Example 7.9.mod 1 tuple Region { 2 key string name; 3 string description; 4} 5 {Region} regions = ...; 6

128

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

7 Seperating model and data

tuple Plant { key Region region; string name; int capacity; float cost; } {Plant} plants with region in regions = ...; float freightCosts[regions][regions] = ...; tuple Order { key Region region; int number; } {Order} orders with region in regions = ...; float forecast = ...; dvar int+ production[plants][orders]; dexpr float costProduction = sum(a in plants, b in orders) a.cost*production[a][b]; dexpr float costTransportation = sum(a in plants, b in orders) freightCosts[a.region][b.region]*production[a][b]; minimize costProduction + costTransportation; subject to { //Orders forall (b in orders) b.number*forecast = sum(b in orders) production[a][b]; }

To illustrate explicit initialization with a data file, Example 7.9a.dat shows the complete data for the case study’s model describing a certain future scenario. First, the various regions are defined, as shown in the previous example (lines 1 to 7). Then, in lines 8 to 11, the two plants in the case study are defined—one in Europe (EU region, with high production capacity but also relatively high production costs) and one in Asia (AS region, a small plant with lower production costs). Next, the freightCosts are defined, whereby only the costs from the two plant location regions—EU and AS—to all regions are required. Thus, all other values of the matrix are filled with 0 by default. This is irrelevant, since the corresponding entries are not used in the model anyway. The procedure is similar to Example 6.27 in Chapter 6. A more "data-saving" alternative,

7.4 Solution of Case Study 5

129

similar to Example 6.29, would be not to read in a matrix, but to directly read in a tuple set that contains only an entry for feasible transport routes—similar to the corresponding data type transportRoutes in Example 7.7.dat or Example 7.8.dat. This is followed by the initialization of the set of orders, which, for this scenario, is first filled with the previous year’s numbers. Then the parameter forecast is set—for this scenario to 100 %. Thus, the scenario that the orders would develop exactly as in the previous year is examined. If another scenario is to be tested, one only has to create a (further) data file in which the entries orders and/or forecast are modified accordingly.

Example 7.9a.dat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

regions = { , , , ,

}; plants = { ,

}; freightCosts = #[ :[30, 40, 20, 50, 50], :[50, 50, 60, 60, 20] ]#; orders = { , , , ,

}; forecast = 1;

With a forecast value of 100 %, the cost minimum of EUR 440,500 is achieved with the following optimal solution: production = [[200 100 300 50 100], [0 0 0 0 100]]. The results of Case Study 5 are listed for all forecast values in Table 7.7. Table 7.7 Results of Case Study 5

forecast 0.8 0.9 1.0 1.1 1.2

Objective function value 349 800 395 150 440 500 485 850 531 200

production [[160 80 240 40 60], [0 0 0 0 100]] [[180 90 270 45 80], [0 0 0 0 100]] [[200 100 300 50 100], [0 0 0 0 100]] [[220 110 330 55 120], [0 0 0 0 100]] [[240 120 360 60 140], [0 0 0 0 100]]

130

7 Seperating model and data

Fig. 7.3 shows exactly the same data as Example 7.9a.dat—prepared in an Excel file. The Excel file is linked to the corresponding data file in Example 7.9b.dat. In the data file (Example 7.9b.dat), it is noticeable that named cell ranges from the Excel file are now used here. As Tip 13 shows, this procedure leads to a further modularization of the model components. Fig. 7.3 gives one of the named cell ranges. The cell range A16 to D17 is selected, and the array at the top left shows the name of this cell range "plants." For better illustration, the named cell ranges are each highlighted in gray in Fig. 7.3. Table 7.8 lists all the named cell ranges of the Excel file.

Figure 7.3 Screenshot of an Excel spreadsheet ("OplBookExample7.9.xlsx") with data for the case study (cf. the corresponding data file in Example 7.9b.dat)

Example 7.9b.dat 1 2 3 4 5 6 7

SheetConnection sheet("OplBookExample7.9.xlsx"); regions from SheetRead(sheet, "regions"); freightCosts from SheetRead(sheet, "freightCosts"); orders from SheetRead(sheet, "orders"); plants from SheetRead(sheet, "plants"); forecast from SheetRead(sheet, "forecast");

7.5 Exercises

131

Table 7.8 Named cell ranges of the Excel file from Fig. 7.3

Name regions freightCosts orders plants forecast Tip 14

Cell range A7:B11 B22:F26 D7:E11 A16:D17 B2

Execution of model and data file with oplrun

As already shown in Section 2.3.3, the command prompt command oplrun.exe can be used to solve models outside the Studio. There, a simple model file was specified as a parameter for oplrun.exe. The command can also be used to flexibly solve model files with different data files. The respective data files have to be specified as additional parameters. With the following call, oplrun.exe loads and executes the model file oplrunExampleChap7.mod with the data file oplrunExampleChap7.dat: oplrun.exe oplrunExampleChap7.mod oplrunExampleChap7.dat In addition, individual data elements can also be set with the command line: oplrun.exe -D forecast=1.1 oplrunExampleChap7.mod oplrunExampleChap7.dat If in this example the model parameter forecast has already been set in the data file, it will be overwritten by the command line parameter ("-D forecast=1.1"). With this approach—i.e., by means of a batch file (cf. Section 2.3.3)—several optimizations with different input parameters can also be carried out automatically. The production manager can easily test different future scenarios—e.g., for 100 %, 110 %, and 120 % forecast orders compared with the previous year's order level.

7.5

Exercises

1. Name and explain the two basic ways of initializing data in OPL. 2. Can multiple data files be used simultaneously in a run configuration? If so, what should be taken into account here? 3. In which order does OPL initialize the data elements? What is meant by lazy initialization? 4. Ranges cannot be initialized in data files. What is the workaround for this problem? 5. What must be considered when you want to use an Excel file to write data into the file?

132

7 Seperating model and data

6. What is the following model file’s output? (Note: The corresponding data file’s content is given in the comments.) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

int int int int int int int int

nCows = ...;//data file: nCows = 12; nPigs = 5; nChickens; nDogs = ...;//data file: nDogs = 2; nCats = 10; nQuadrupeds = nCows + nPigs + nDogs + nCats; nBipeds = nChickens; nLegs = 4*nQuadrupeds + 2*nBipeds;

execute { writeln(nCows, " ", nPigs, " ", nChickens); writeln(nBipeds, " ", nQuadrupeds); nCats = 2*nChickens; nChickens = 15; writeln(nLegs); nDogs = 3; nBipeds = nChickens; writeln(nBipeds, " ", nQuadrupeds); } execute { nChickens = 6; writeln(nBipeds, " ", nQuadrupeds); }

7. Consider the following data file. Specify a corresponding model file in which these data elements are declared: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

nWarehouses = 3; nCustomers = 3; nProducts = 5;

//Number of warehouses //Number of customers //Number of products

price = #[2:1.4 5:3.4 1:2.0 4:6.3 3:2.7]#; //product price transportCostsFixed = [[1, 2, 6], [2, 8, 3], [5, 9, 1]]; //fixed transport costs: warehouse -> customer demand = #[ 1:[4, 7, 1, 3, 4], 2:[8, 3, 1, 7, 0], 3:[1, 3, 9, 3, 2] ]#; //product-specific customer demand customers = { ##, , ## }; //customer data

8. Create a data file that reads all data items from the following Excel file stored in the OPL project folder under the name "Variables_Scenario1.xlsx" and writes the resulting data to the Excel file:

7.5 Exercises

133

8

Selected features of OPL and CPLEX Optimization Studio

After introducing the basic language elements of OPL in the previous chapters, the focus is now on three specific features of OPL and the CPLEX Optimization Studio: (i) working with logical operators, (ii) implementing piecewise-defined functions, and (iii) analyzing infeasible models including approaches to correction. The following case study is given to illustrate. Section 8.4 presents the complete solution to the case study.

RideEasy Case Study 6: Major customer order in South America RideEasy receives an order request from a major new customer in South America who is an important player on the market. The order depends on all products being manufactured exclusively by the plant in South America. The customer has specified a minimum requirement for his order (at least 300 units of the product U-A, 200 of U-B, and 250 of U-C) but would—if available—also accept more units within certain limits (cf. Table 8.1). With known unit prices (cf. Table 8.1 again), the production manager now wants to determine whether RideEasy should accept this request and, if so, how many bicycle models they could produce within the specified limits of the order. To win this major customer, all the resources of the South American plant have to be dedicated exclusively to this order, and all other customer requests have to be manufactured in the other plants distributed around the world. The other plants have sufficient free capacity; therefore, no opportunity costs need to be taken into account. Three machines are available for production. Table 8.2 lists the machine capacity available for the individual machines in the planning period, as well as the machine time required to produce one bicycle, for each bicycle type. If necessary, the production

© Springer-Verlag GmbH Germany, part of Springer Nature 2022 S. Nickel et al., Decision Optimization with IBM ILOG CPLEX Optimization Studio, Graduate Texts in Operations Research, https://doi.org/10.1007/978-3-662-65481-1_8

135

136

8 Selected functionalities of OPL and CPLEX Optimization Studio

manager can restructure the production process before production starts. This would result in the regular machine capacities simultaneously expanding (cf. Table 8.2) to 260 hours (M1), 410 hours (M2), and 470 hours (M3) in conjunction with higher wage costs to the total of EUR 6,000 (weekend supplements). With or without restructuring, the production manager set the requirement that if the company accepted this customer’s order, which means RideEasy forgoing other orders in South America, at least one of the three machines would have to be fully utilized. The first row of Table 8.3 shows the production costs for the three products, which are fixed. However, based on the regular packaging capacities, there is a predetermined regular maximum production quantity for each product (U-A less than 320 units, U-B less than 220 units, and U-C less than 270 units). If the actual production quantity of a product reaches or exceeds this threshold, the respective production costs increase because an additional packing station has to be set up to accommodate this level of production (cf. bottom row in Table 8.3). Still, the total production quantity of bicycles remains (indirectly) limited upward by the machine capacities. The deliveries are carried out partly by internal and partly by external service providers, and the delivery costs depend on the total number of bicycles to be delivered (cf. Table 8.4). For a total number of up to 100 bicycles, fixed costs are incurred once (in-house delivery), after which the additional delivery costs incrementally increase depending on the quantity because the next (more expensive) service provider has to be used. The production manager wants to use a suitable model to check whether the new customer's order can be handled and should be accepted. Initially, the model should show possibilities without restructuring; if necessary, the model should be adjusted to show options with restructuring. The objective is to maximize the contribution margin.

Table 8.1 Minimum and maximum demand of the major customer

Product U-A U-B U-C

Demand minimum 300 200 250

Demand maximum 350 250 300

CM [EUR/bike] 350 410 460

Table 8.2 Machine capacity and machine time requirements

Machine M1 M2 M3

Capacity [h] without/ with restructuring 200/260 400/410 450/470

Demand UA [h/bike] 0.2 0.3 0.5

Demand U-B [h/bike] 0.4 0.2 0.5

Demand U-C [h/bike] 0.3 0.6 0.7

8.1 Modeling with logical operations

137

Table 8.3 Volume-based scaling of production costs

Product U-A Quantity < 320 ≥ 320

Product U-B

Product U-C

Cost

Quantity

Cost

Quantity

Cost

50,000 54,000

< 220 ≥ 220

30,000 33,000

< 270 ≥ 270

40,000 45,000

Table 8.4 Quantity-based delivery costs

Type of costs Fixed costs

Number of bicycles {1, 2, 3, …, 100}

Amount [EUR] 2,000

Variable unit costs Variable unit costs

{101, 102, …, 400} {401, 402, …, 700}

10 per bike 15 per bike

Variable unit costs

{701, 702, … }

20 per bike

8.1 Modeling with logical operations OPL relies on the logical operators known in propositional calculus and, thus, enables logical operations. Logical operators can be used to link linear constraints in such a way that complex constraints result in logical statements. The syntax of the operators is similar to that of other programming languages, such as Haskell or C. Statements can only assume one of the two states true or false. There are no further states—this is referred to as the law of excluded middle. Also, the states cannot be assumed simultaneously—referred to as the law of non-contradiction. Thus, logical statements can be evaluated on the basis of a Boolean truth-value, returning a 1 for a true statement or a 0 for a false statement. The elementary building blocks of complex logical statements in OPL are statements formed in terms of linear constraints, as discussed in the previous chapters. The relational operators introduced in Table 3.7 (Section 3.4) are available for this purpose. Depending on whether the respective constraint is valid or not, a truth-value of true or false (i.e., 1 or 0) is returned. 8.1.1 Logical operators in OPL Table 8.5 provides an overview of the various logical operators in OPL, followed by their definitions, each of which is supported by a corresponding truth table. Also, their precedence is given.

138

8 Selected functionalities of OPL and CPLEX Optimization Studio

Table 8.5 Logical operators in OPL

Operator ! && || != => ==

Description Negation ("logical not") Conjunction ("logical and") Disjunction ("logical or") Exclusive disjunction ("exclusive or") Implication Equivalence

In a truth table, all combinations of the statements’ truth-values (true and false) are listed: 2 = 2 combinations when only one statement is presented (cf. Table 8.6), 2 = 4 when two statements are linked (cf. Table 8.7), 2 = 8 when three statements are combined (cf. Table 8.14), etc. A statement !A (pronounced "not A") is called the negation of the statement A. The statement !A is true if A is false, and !A is false if A is true (cf. Table 8.6). Table 8.6 Truth table of negation

Statement A !A

Truth-Value 1 0 0 1

The statement A && B (pronounced "A and B") is called conjunction. Only if both statements A and B are true, A && B is also true; otherwise, A && B is false (cf. Table 8.7). Table 8.7 Truth table of conjunction

Statement A B A && B

Truth-Value 1 1 0 1 0 1 1 0 0

0 0 0

The statement A || B (pronounced "A or B") is called disjunction. If at least one of the statements A, B is true, then A || B is also true. If both statements A and B are false, then A || B is also false (cf. Table 8.8).

8.1 Modeling with logical operations

139

Table 8.8 Truth table of disjunction

Statement

Truth-Value

A

1 1

1 0

0 1

0 0

1

1

1

0

B A || B

The statement A != B (pronounced "either A or B") is called exclusive disjunction. Only if exactly one of the statements A, B is true, then A != B is true. If both statements A and B are true or both statements are false, then A != B is false (cf. Table 8.9). Table 8.9 Truth table of exclusive disjunction

Statement

Truth-Value

A B

1 1

1 0

0 1

0 0

A != B

0

1

1

0

The syntax of the exclusive disjunction has already been dealt with in Section 3.4 (cf. Table 3.7). Thus, depending on the context, the operator "!=" can be used on the one hand as a relational operator and on the other hand as a logical operator. As relational operator, the expressions are compared on both sides of the operator—in this case, there would be no statements A and B, but numerical values, which result from the evaluation of the expressions. These must be unequal according to the operator. As a logical operator, "!=" requires the combination of a true and a false truth-value, as Table 8.9 shows. Since true statements are evaluated with the value 1 and false statements with the value 0, logical statements can be traced back to relational comparisons within the program. The statement A => B (pronounced "if A, then B") is called implication. Only if statement A is true and B is false, can A => B be false; in all other cases, A => B is true (cf. Table 8.10). Table 8.10 Truth table of implication

Statement A B A => B

Truth-Value 1 1 0 1 0 1 1 0 1

0 0 1

140

8 Selected functionalities of OPL and CPLEX Optimization Studio

The statement A == B (pronounced "A is equivalent to B") is called equivalence. It is true if the statements A and B have equal truth-values; otherwise, A == B is false (cf. Table 8.11). Like the syntax of the exclusive disjunction, the syntax of logical equivalence has multiple uses and is also used as a relational operator (equality). A separate example for the different applications is given in Section 8.1.3. Table 8.11 Truth table of equivalence

Statement A B A == B

Truth-Value 1 1 0 1 0 1 1 0 0

0 0 1

Like basic arithmetic operations (e.g., exponentiation precedes multiplication, multiplication precedes addition, etc.), there are also rules of operator precedence when performing logical operations (cf. Table 8.12). Negation takes the highest precedence. Equivalence and exclusive disjunction precede conjunction and disjunction. Implication takes the lowest precedence in OPL. Table 8.12 Precedence of logical operators in OPL

Precedence level 1 2 3 4 5 6

Logical operation Negation ("!") Equivalence ("==") Exclusive disjunction ("!=") Conjunction ("&&") Disjunction ("||") Implication ("=>")

As with basic arithmetic operations, logical operations can be placed in brackets to change the default precedence. Operators of equal precedence level are evaluated from left to right. For instance, the compound statement A => B => C implicitly has the following bracketing: (A => B) => C. Further explanations of logical operators can be found in Chen et al. (2010), Section 3.1.2, Grieser (2018), Section 7.1, and Sydsæter et al. (2021), Section 1.2, among others. Tip 15

Using brackets when working with logical operators in OPL

To design your own program code in such a way that it is as easy and intuitive to read as possible, it is advisible to use rather too many brackets than only the absolutely

8.1 Modeling with logical operations

141

necessary ones when working with logical operations. This also helps avoid uncertainties regarding the precedence of logical operators. 8.1.2 Modeling with logical operators In the context of modeling mixed-integer linear programs, the use of logical operators is first demonstrated in the following example for the frequently occurring case in which each of the constraints to be linked directly returns the value of a binary variable from the model as a truth-value (Example 8.1). Subsequently, linking binary variables with a "real" linear constraint (Example 8.2) or linking two "real" linear constraints (Example 8.3) is considered. Example 8.1 contains six (logical) constraints (lines 5 to 10) in which the logical operators presented in Section 8.1.1 are used. In this example, six products are available from which a subset is to be ordered/selected, which must satisfy the imposed logical constraints. It is "merely" a feasibility problem, which is why an objective function is not used (cf. Section 3.3, last paragraph).

Example 8.1 1 {int} products = {1, 2, 3, 4, 5, 6}; //6 different products 2 dvar boolean order[products]; 3 4 subject to { 5 !(order[1] == 1); 6 (order[2] == 1) && (order[3] == 1); 7 (order[4] == 1) || (order[5] == 1) || (order[6] == 1); 8 (order[5] == 1) != (order[6] == 1); 9 (order[3] == 1) => (order[5] == 1); 10 (order[4] == 1) == (order[6] == 1); 11 }

First, in line 1, the set products of data type int is declared and initialized. In line 2, binary decision variables order[products] are declared and take the value 1 if an order is placed and the value 0 otherwise. The first constraint (line 5) specifies that product 1 is not to be ordered—of course, the following alternative formulation would be identical in content: order[1] == 0. Line 6 requires that both product 2 and product 3 be included in the order. Line 7 requires that at least one of products 4, 5, and 6 be included in the purchase order. Line 8 ensures that either product 5 or product 6 is ordered. Line 9 contains an implication: If product 3 is ordered, then product 5 must also be ordered. Line 10 requires the same truth-values for ordering products 4 and 6: Either both or neither of the products should be ordered. This small example can quickly be grasped through mental calculation: The only feasible solution is to order products 2, 3, and 5.

142

8 Selected functionalities of OPL and CPLEX Optimization Studio

Tip 16

Syntax errors in logical operators applied to decision variables

Applying the logical operators "&&", "||", and "=>" to decision variables leads to syntax errors—e.g., if the formulation (order[2] && order[3]) were used instead of the correct formulation ((order[2] == 1) && (order[3] == 1)) in Example 8.1, line 6. For the constraint in line 8, however, an alternative representation is possible. For example, (order[5] != order[6]) leads to the same result as the exclusive disjunction (order[5] == 1) != (order[6] == 1). However, in this case, the operator "!=" is used not as a logical operator but as a relational operator. The same applies to the equivalence in the middle of line 10, which links the two expressions in brackets. In addition to the binary decision variables order[products], Example 8.2 contains the integer, non-negative decision variables orderQuantity[products], which represent the order quantity (line 3). In each of the constraints (lines 6 and 7), there is a logical operator (implication) between the expressions in brackets that links a binary variable to the truth-value of a "real" linear constraint—in this case, the relational comparison of orderQuantity[2] with a certain threshold. If product 2 is ordered, then the order quantity should be at least 50 units, otherwise 0.

Example 8.2 1 2 3 4 5 6 7 8

{int} products = {2}; dvar boolean order[products]; dvar int+ orderQuantity[products]; subject to { (order[2] == 1) => (orderQuantity[2] >= 50); (order[2] == 0) => (orderQuantity[2] == 0); }

In Example 8.3, a logical operator (implication) links two "real" linear constraints (line 5): If at least 200 units of product 2 are ordered, then the number of product 1 units ordered should be less than or equal to 80.

Example 8.3 1 2 3 4 5 6

{int} products = {1, 2}; dvar int+ orderQuantity[products]; subject to { (orderQuantity[2] >= 200) => (orderQuantity[1] (orderQuantity[i] == 0); (order[i] != 0) => (orderQuantity[i] >= 1); // (order[i] == 0) == (orderQuantity[i] == 0); } (orderQuantity[1] = 1 x[1] + x[2] == 1 x[1] != x[2] (alternative) x[1] ((order[1] == 1) && (order[2] == 1)); A linearized variant results from the following considerations: order[3] must be forced to 0 if only one or neither of the other two products is ordered. If both products are ordered, there must be no restriction with respect to the value of order[3]. This is ensured by the following two constraints: order[3] (orderQuantity[2] == 0); can be linearized as follows (Big M represents a sufficiently large positive number): 50*order[2] (orderQuantity[1] = 1; 14 sum(i in products) (orderQuantity[i] >= 40) 10; 20 -> 30; 40 -> 50; 60}; 3 4 assert deliveryCosts(9) == 0; 5 assert deliveryCosts(10) == 20;

The step function shown in Fig. 8.1, which depicts delivery costs as a function of the delivery quantity x, serves as an illustration. After the corresponding OPL keyword stepwise, the individual sections of the function are enumerated and separated by semicolons (cf. Example 8.6, line 2). More precisely, in the curly brackets, for each section a value to the left and one to the right of an "arrow" is given. The values to the left of the arrows indicate the function value, which is assigned to each element of the respective interval. The values to the right of the arrows represent the upper interval endpoints, which are always excluded (half-open intervals). The lower interval endpoint of the first interval is always −∞ and not explicitly specified. The other lower interval endpoints are implicitly the upper endpoints of the preceding intervals. The upper endpoint of the last interval is always +∞. In the example, the function deliveryCosts has function values of 0 in the interval (−∞; 10), function values of 20 in the interval [10; 30), function values of 40 in the interval [30; 50), and function values of 60 in the interval [50; +∞). That the delivery cost for 10

8.2 Piecewise linear functions

149

units is 20 (and not 0) is confirmed by the assert command in line 5 (for assert commands, see Tip 8 in Section 4.2.3). With the help of arrays for the function values and the upper interval endpoints, generic modeling is possible as an alternative to Example 8.6, as Example 8.7 illustrates.

Example 8.7 (for Fig. 8.1) 1 2 3 4 5 6 7 8 9 10

int n = 3; int functionValue[1..n+1] = [0, 20, 40, 60]; int upperIntervalEndpoint[1..n] = [10, 30, 50]; stepFunction deliveryCostsAlternative = stepwise(i in 1..n) { functionValue[i] -> upperIntervalEndpoint[i]; functionValue[n+1] }; assert deliveryCostsAlternative(9.99) == 0; assert deliveryCostsAlternative(10.0) == 20;

In an optimization model, a piecewise linear function can be integrated as an objective function or as a constraint by linking it appropriately to a decision variable. This is done by appending the decision variable after the curly brackets of the stepwise command— e.g., for the step function in Example 8.6 used as the objective function to be maximized by: maximize stepwise {0 -> 10; 20 -> 30; 40 -> 50; 60} x; When using an already defined function, the decision variable of the function must be passed as an argument in round brackets (cf. line 6 in Example 8.8). In Example 8.8, the function value of the delivery cost function from Fig. 8.1 has to be maximized, under the constraint that the delivery quantity x is at most 35 units (maxDeliveryQuantity = 35). The optimal objective function value of 40 for any optimal solution x ∈ [30, 35] units can be confirmed visually by looking at the plot in Fig. 8.1.

Example 8.8 (for Fig. 8.1) 1 2 3 4 5 6 7 8 9 10

include "example_8.06.mod"; int maxDeliveryQuantity = 35; dvar int x; maximize deliveryCosts(x); subject to { x (15*i) + (5*k); 0 8 };

8.2.2 General piecewise linear functions The special case of a step function is now generalized to piecewise linear functions (pwlFunction); both continuous functions and functions with jump points are considered in what follows. Similarly to step functions, they can be integrated into optimization models (cf. Example 8.8). Continuous functions The function represented in Fig. 8.3 serves to illustrate a continuous function. Example 8.10 contains a possible corresponding OPL command. It shows that the OPL syntax for continuous functions is similar to the syntax of the step functions dealt with in the previous section.

Example 8.10 (for Fig. 8.3) 1 pwlFunction deliveryCostsContinuous = piecewise { 2 0 -> 10; 1 -> 20; .5 -> 30; 0 3 } (5, 0);

8.2 Piecewise linear functions

151

deliveryCostsContinuous(x) 15 10

10

20

30

x

Figure 8.3 Continuous function

As with step functions, the domain is always the set of all real numbers from (−∞; +∞). The interval lower endpoint of the first interval is −∞ and is not explicitly given. The further interval lower endpoints in each case result implicitly as the upper endpoints of the preceding intervals. The upper endpoint of the last interval is +∞. After the keyword piecewise, the values to the left of the "arrows" now represent the slope of the respective sections of the function, the values to the right of the arrows—as with the step functions—the respective (excluded) upper interval endpoints. At the end of the command, an anchor point can optionally be added (in the example the point (5 , 0), see line 3), through which the function is to pass in its first section. This defines a "vertical anchoring" of the entire function. Without specification, (0, 0) is set as the default anchor point, which in the Example would lead to the same curve (cf. Example 8.11). The function from the example thus has in the interval (−∞; 10) a slope of 0 and passes through the point (5, 0). At the abscissa value 10, the function has a kink point. This is the starting point for the second section of the function—in the interval [10; 20)—with the slope 1. The next kink point at the abscissa value 20 is the starting point for the third section in [20; 30) with slope 0.5, and at the abscissa value 30 the fourth section in [30; +∞) with slope 0 starts. As an alternative, with the help of arrays for the function values and the upper interval endpoints, generic modeling is also possible here, as Example 8.11 illustrates.

Example 8.11 (for Fig. 8.3) 1 2 3 4 5 6 7

int n = 3; int upperIntervalEndpoint[1..n] = [10, 20, 30]; float slope[1..n+1] = [0, 1, .5, 0]; pwlFunction deliveryCostsContinuousAlt = piecewise(i in 1..n) { slope[i] -> upperIntervalEndpoint[i]; slope[n+1] };

152

8 Selected functionalities of OPL and CPLEX Optimization Studio

Functions with jump points A piecewise linear function can also contain jump points, as is the case with the function in Fig. 8.4 at the values = 10 and = 20. If a jump occurs at the upper interval endpoint of a section of a function, the endpoint (to the right of the "arrows") must be addressed twice in the piecewise command. Therefore, the syntax of Example 8.10 must be supplemented to provide for jumps with a second specification of the corresponding upper interval endpoints (cf. Example 8.12): In the first specification, the value to the left of the arrow corresponds to the slope, and in the second specification, to the jump along the ordinate. deliveryCostsDiscontinuous 30 25

15

5

10

20

30

Figure 8.4 Function with jump points

Example 8.12 (for Fig. 8.4) 1 pwlFunction deliveryCostsDiscontinuous = piecewise { 2 0 -> 10; 5 -> 10; 1 -> 20; 10 -> 20; .5 -> 30; 0 3 };

With the help of arrays for the function values and the upper interval endpoints, generic modeling is again possible, as Example 8.13—analogous to Example 8.11—illustrates. If one were to add the commented-out part in line 11, a syntactically correct modeling would also result, even if the artificial specification of a "jump" by 0 units is, of course, not necessary for intervals without jump.

8.2 Piecewise linear functions

153

Example 8.13 (for Fig. 8.4) 1 2 3 4 5 6 7 8 9 10 11 12 13

int upperIntervalEndpoint[1..3] = [10, 20, 30]; float slope[1..4] = [0, 1, .5, 0]; float jump[1..3] = [5, 10, 0]; pwlfunction deliveryCostsDiscontinuousAlt1 = piecewise { slope[1] -> upperIntervalEndpoint[1]; jump[1] -> upperIntervalEndpoint[1]; slope[2] -> upperIntervalEndpoint[2]; jump[2] -> upperIntervalEndpoint[2]; slope[3] -> upperIntervalEndpoint[3]; //jump[3] -> upperIntervalEndpoint[3]; slope[4] } (0,0);

An even more general and compact formulation is given in Example 8.14.

Example 8.14 (for Fig. 8.4) 1 2 3 4 5 6 7 8 9

int n = 3; int upperIntervalEndpoint[1..n] = [10, 20, 30]; float slopeJump[1..2][1..n] = [[0, 1, .5], [5, 10, 0]]; float lastSlope = 0; pwlfunction deliveryCostsDiscontinuousAlt2 = piecewise (j in 1..n, i in 1..2) { slopeJump[i][j] -> upperIntervalEndpoint[j]; lastSlope };

Finally, it should be noted that the values of the upper interval endpoints must be specified monotonically increasing, and that sections consisting of a single point cannot be modeled. 8.2.3 Modeling piecewise linear functions by linear constraints First, general linearization techniques are presented. Then, for frequently occurring special cases, we demonstrate that each of them can be further simplified. In addition to the literature referred to in the explanations, the following can also be consulted: Chen et al. (2010), Section 3.3; Kallrath (2021), Section 6.8.2; Van Hentenryck (1999), Section 9.4; and Williams (2013), Section 7.3. General linearization techniques In the following sections, two alternative techniques are discussed that can be used to linearize piecewise linear functions in general. On the one hand, convex combinations can be used within each section of the function, and on the other hand,

154

8 Selected functionalities of OPL and CPLEX Optimization Studio

=

=

=

Figure 8.5 "Convex combinations" technique

=

"decomposition of the -variable" can be performed according to the domains of the individual sections. In the first technique ("convex combinations," cf. Sherali 2001), for the left and right endpoints of the intervals of each section ∈ {1, … , } of the function, the two variables are related to the corresponding -coor, ≥ 0 are defined (cf. Fig. 8.5). and dinates ( or ) and the -coordinates ( or ) of the endpoints. In this way, any arbitrary -value, as well as the corresponding function value = ( ) of the function, can be expressed as variables in the form of two constraints that form the pairwise convex combinations of the endpoints: =∑

[

+

]

or

=∑

[

+

]

In addition, it must be ensured that (1) for each section, these are actually convex combi= 1, if the -value nations, i.e., points on the line connecting the two endpoints ( + lies in the interval ), and that (2) only one summand of the summation formulas from the right-hand side of the two constraints above is assigned a value not equal to 0 (corresponding to the interval in which the affected -value actually lies). Both requirements (1) and (2) are taken care of by additional binary variables ( ∈ {0; 1}) and additional constraints = for ∈ {1, … , }, with an additional constraint ∑ + = 1 ensuring that only one section of the function is "activated." In the second technique (" -decomposition", cf. Domschke and Drexl 1996, pp. 56 ff.), the -variable is composed of -variables defined individually for the sections of the function, i.e.,

8.2 Piecewise linear functions

155

Figure 8.6 " -decomposition" technique

(cf. Fig. 8.6): = ∑ . The appropriate -value is determined by considering the slopes associated with the respective -sections, and then calculating = ∑ . In addition to these two constraints, further constraints ensure that the -variables take on ) of the respective section a value less than or equal to the width of the interval ( − ) for ) in the function: ≤( − = 1 and ≤( − for ∈ {2, … , } with ∈ {0, 1} . The binary variables —with additional constraints ( − ) ≤ for all ∈ {1, … , ( − 1)}— ensure the successive "activation" of the sections. The example given in Fig. 8.7 is intended to illustrate the two techniques. Using "convex combinations," the piecewise linear function shown in Fig. 8.7 results in a linear formulation for which six linear constraints (ct1 to ct6) are required: = 0 + 300 + 300 + 200 = 0 + 100 + 100 + 300 + = + = + = + + =1 , , ∈ {0, 1} , , , , , , , ≥0

+ 200 + 300

+ 700 + 600

(ct1) (ct2) (ct3) (ct4) (ct5) (ct6) (ct7) (ct8)

156

8 Selected functionalities of OPL and CPLEX Optimization Studio

deliveryCosts 700

300 200

100

300

600

Figure 8.7 Example instance of a piecewise linear function

Example 8.15 contains the corresponding program code for the implementation using "convex combinations."

Example 8.15 1 range i = 1..3; 2 {string} corners = {"L", "R"}; 3 4 dvar boolean delta[i]; //(ct7) of the mathematical model 5 dvar float+ x; //(ct8) 6 dvar float+ y; //(ct8) 7 dvar float+ lambda[i][corners]; //(ct8) 8 9 subject to { 10 ct1: y == 0*lambda[1]["L"] + 300*lambda[1]["R"] + 300*lambda[2]["L"] 11 + 200*lambda[2]["R"] + 200*lambda[3]["L"] + 700*lambda[3]["R"]; 12 ct2: x == 0*lambda[1]["L"] + 100*lambda[1]["R"] + 100*lambda[2]["L"] 13 + 300*lambda[2]["R"] + 300*lambda[3]["L"] + 600*lambda[3]["R"]; 14 ct3: lambda[1]["L"] + lambda[1]["R"] == delta[1]; 15 ct4: lambda[2]["L"] + lambda[2]["R"] == delta[2]; 16 ct5: lambda[3]["L"] + lambda[3]["L"] == delta[3]; 17 ct6: delta[1] + delta[2] + delta[3] == 1; 18 }

Using the " -decomposition," a linear formulation with seven linear constraints (ct1 to ct7) is obtained for the piecewise linear function shown in Fig. 8.7:

8.2 Piecewise linear functions

=3



+

= + + 100 ≤ 200 ≤ ≤ 100 ≤ 200 ≤ 300 , ∈ {0, 1} , , , , ≥0

157

(ct1) (ct2) (ct3) (ct4) (ct5) (ct6) (ct7) (ct8) (ct9)

The corresponding program code for the implementation using " -decomposition" can be found in Example 8.16.

Example 8.16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

range i = 1..3; range j = 2..3; dvar dvar dvar dvar

boolean delta[j]; float+ xTotal; float+ x[i]; float+ y;

subject ct1: ct2: ct3: ct4: ct5: ct6: ct7: }

//(ct8) of the mathematical model //(ct9) //(ct9) //(ct9)

to { y == 2*x[1] - (1/2)*x[2] + (4/3)*x[3]; xTotal == x[1] + x[2] + x[3]; 100*delta[2] 0) && (line.indexOf("//") != 0)) { 13 var array = line.split(","); 14 command(array); 15 } 16 } 17 fileObject.close(); 18 } else { 19 writeln("Error: The file '" + file + "' does not exist!"); 20 } 21 } 22 }

Example 9.5.mod shows the implementation of the function loadCSV(). The crucial difference between Examples 9.4.mod and 9.5.mod lies in line 14 where command is a function variable. The content of this variable is a function, which is passed as second parameter when calling loadCSV() from "outside" (line 5). Example 9.6.mod now shows the flexible multiple use or re-use of the function defined in Example 9.5.mod. In the first IBM ILOG Script block, the CSV file "example_9.04.csv" is read in to initialize the set of products. For this, the function func1() implements the data-specific part. For each line of the CSV file, the first value (array[0]) from the read array is used as the product name and added to the set products. Next, the declaration of the OPL arrays type, price, and weight can be done in the OPL program code (lines 16 to 18). In the second IBM ILOG Script block, "example_9.04.csv" is read in again and then processed using the function func2(), which fills the OPL arrays type, price, and weight from the read array[] accordingly.

180

9 Selected functionalities of IBM ILOG Script

Example 9.6.mod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

include "example_9.05.mod"; {string} products = {}; execute { //loading of all product names from the file "example_9.04.csv" // and writing them into the set products function func1(array) { products.add(array[0]); } //the file "example_9.04.csv" is loaded and for each line // the function func1() is called loadCSV("example_9.04.csv", func1); } string type[products]; float price[products]; float weight[products]; execute { //loading of all further data from the CSV file // and writing it in the respective arrays function func2(array) { type[array[0]] = array[1]; price[array[0]] = array[2]; weight[array[0]] = array[3]; } //re-loading "example_9.04.csv" and calling the function // func2() for each line loadCSV("example_9.04.csv", func2); }

Output in the Scripting log tab for Example 9.6.mod: Read example_9.04.csv //name,type,price,weight U-A,Special,1800,20 U-B,Special,2000,22 M-100,Mountain,600,11 M-200,Mountain,900,8 Read example_9.04.csv //name,type,price,weight U-A,Special,1800,20 U-B,Special,2000,22 M-100,Mountain,600,11 M-200,Mountain,900,8

9.2 Pre- and Postprocessing with IBM ILOG Script

9.2

181

Pre- and Postprocessing with IBM ILOG Script

A common use of IBM ILOG Script is to support the preprocessing of data (cf. introductory text in Chapter 3). To do this, corresponding IBM ILOG Script blocks are placed in the model file before the actual model. Some application examples for preprocessing by using IBM ILOG Script have already been covered—e.g., to load a CSV file and fill data elements (Example 9.4.mod) or to calculate product prices (Example 5.9). In addition to its application in preprocessing, IBM ILOG Script is often used in postprocessing for result preparation and the preparation of output (cf. Chapter 5). In the model file, corresponding IBM ILOG Script blocks are located after the actual model. After optimization, a number of status functions and properties of the cplex object (cf. Table 9.4), decision variables (cf. Table 9.5), and constraints (cf. Table 9.6) are available for use in IBM ILOG Script during postprocessing.

Example 9.7.mod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

{string} products = {"U-A", "U-B", "M-100", "M-200"}; int order[products] = [100, 200, 300, 400]; dvar float+ production[products]; minimize sum(p in products) production[p]; subject to { forall (p in products) ct: production[p] >= order[p]; } execute { writeln("Objective function value: " + cplex.getObjValue()); for (var p in products) { writeln("product " + p + ": production " + production[p]); } for (var p in products) { writeln(production[p].name + " " + production[p].LB + " " + production[p].UB + " " + production[p].solutionValue + " " + production[p].reducedCost); writeln(ct[p].name + " " + ct[p].LB + " " + ct[p].UB + " " + ct[p].dual + " " + ct[p].slack); } }

Output in the Scripting log tab for Example 9.7.mod: // solution (optimal) with objective 1000 Objective function value: 1000 product U-A: production 100 product U-B: production 200 product M-100: production 300

182

9 Selected functionalities of IBM ILOG Script

product M-200: production 400 production["U_A"] 0 Infinity 100 0 ct["U-A"] 100 Infinity 1 0 production["U_B"] 0 Infinity 200 0 ct["U-B"] 200 Infinity 1 0 production["M_100"] 0 Infinity 300 0 ct["M-100"] 300 Infinity 1 0 production["M_200"] 0 Infinity 400 0 ct["M-200"] 400 Infinity 1 0 Example 9.7.mod contains an IBM ILOG Script block that is located after the subjectto block and is, therefore, executed in postprocessing after optimization. Line 11 outputs the value of the objective function. The subsequent loops iterate over all products p. Starting at line 16, for each product, the name of the variable, the lower and upper bounds of its domain, the value of the variable in the found optimal solution, and the reduced cost are output for the corresponding decision variable production[p]. Then, for each constraint ct[p], the name of the constraint, the lower and upper bounds of the constraint, as well as the shadow price and slack of the found solution, are output. Note that, in order to get the constraint information as output, the constraints have to be named (cf. Section 3.4, Example 3.15). In the example, these constraints have been named ct:, which automatically makes them available in the IBM ILOG Script block in the (identically named) array ct[]. Table 9.4 Functions of the cplex object to query status information in IBM ILOG Script postprocessing

Function cplex.getCplexStatus()

cplex.getObjValue()

Description CPLEX solution status after optimization. Returns the value 1 if the optimization was successful. An overview of all status values is listed in the Studio Help under "Changing option values." Value of the objective function after optimization.

Table 9.5 Properties of OPL decision variables for use in IBM ILOG Script postprocessing

Property v.LB v.name v.reducedCost v.solutionValue v.UB

Description Lower bound of the domain Name of the variable Reduced cost of the solution Value of the variable in the solution Upper bound of the domain

9.3 Runtime measurement and CPLEX settings

183

Table 9.6 Properties of OPL constraints for use in IBM ILOG Script postprocessing

Property or function c.dual c.LB c.name c.slack c.UB

9.3

Description Shadow price of the constraint in the solution Lower bound of the right-hand side’s sensitivity range in the solution Name of the constraint Difference between left- and right-hand side of the constraint (slack) in the solution Upper bound of the right-hand side’s sensitivity range in the solution

Runtime measurement and CPLEX settings

In addition to its correctness, the development of an optimization model often requires that it behaves as performantly as possible. To assess the performance of a model (e.g., regarding the structure of the input data, the actual optimization in CPLEX, or data postprocessing) with different data, runtime measurements have to be performed. IBM ILOG Script can be used to perform such runtime measurements. Table 9.7 shows an overview of the available methods for runtime measurements and when working with timestamps. Table 9. 7 Methods for runtime measurement

Function

Description

cplex.getCplexTime()

Returns a timestamp of data type float in seconds. Returns a deterministic timestamp of the CPLEX optimization (without OPL runtime) in ticks.

cplex.getDetTime()

var now = new Date() now.getTime() now.getYear() now.getMonth() now.getDate() now.getHours() now.getMinutes() now.getSeconds() now.getMilliseconds()

Returns a new data object of type Date with a current timestamp. Current timestamp Year of the timestamp Month of the timestamp Day of the timestamp Hour of the timestamp Minute of the timestamp Second of the timestamp Millisecond of the timestamp

184

9 Selected functionalities of IBM ILOG Script

The function cplex.getCplexTime() returns a timestamp as a floating point number including fractions of a second. Its absolute value is irrelevant, since only the difference between timestamps is of interest when measuring the runtime. The function cplex.getDetTime() returns a deterministic timestamp, measured in CPLEX ticks. A tick corresponds to about one thousandth of a second on many hardware platforms but can also significantly deviate from this. The CPLEX runtime in ticks is independent of the computer’s current workload. It is an attempt to abstract from the current and random load on the executing computer and to always obtain the same "deterministic time" on the same computer or similar computers for the same model and data— given the same solution—and, thus, to be able to reproduce it. One limitation, however, is that it can only measure the time the CPLEX solver itself requires, not the time required for pre- or postprocessing in OPL. Therefore, the function cplex.getDetTime()always returns 0 if it is called before the optimization (i.e., before the subject-to block) and always returns the "deterministic" runtime of the optimization after the optimization. For many purposes, this function is therefore only suitable to a limited extent, since it cannot be used to measure the runtime of model building. The following example demonstrates the use of cplex.getCplexTime(). For this purpose, a separate function is defined, which simplifies the runtime measurement. Example 9.8.mod shows this function called time() for time measurement in IBM ILOG Script. Initially, the timestamps timeInit and timeStamp are set to the start time (line 6). After that, each time the function time() is called, the timestamp is updated, and the total runtime and lap time—the time since the last call of time()—are returned as a string (line 13). In this way, current timestamps can be output via the IBM ILOG Script program code (cf. Example 9.9.mod, line 8 and line 16).

Example 9.8.mod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

float timeInit = 0; float timeStamp = 0; execute { //getCplexTime() returns current timestamp in seconds timeInit = timeStamp = cplex.getCplexTime(); //time() returns a string describing the runtime function time() { var tt = cplex.getCplexTime(); var lap = tt - timeStamp; //lap measures time between calls var total = tt - timeInit; //total measures time since start timeStamp = tt; return "TIME total " + total + "sec lap " + lap + "sec"; } writeln(time()); }

9.3 Runtime measurement and CPLEX settings

185

Example 9.9.mod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

include "example_9.08.mod"; int order = 10; dvar int+ production; execute { for(var i=1; i= order; } execute { writeln(time()); }

Output in the Scripting log tab for Example 9.9.mod: TIME total 0sec lap 0sec TIME total 0.219sec lap 0.219sec // solution (optimal) with objective 10 TIME total 0.25sec lap 0.031sec Another important IBM ILOG Script field of application is to set values of CPLEX parameters to customize the solution process. As already explained in Section 2.3.2, these CPLEX settings can also be adjusted in OPS files. Sometimes, however, one would like to define the parameters directly in the OPL model, e.g., if they are to be selected dynamically depending on input data. Example 9.10.mod shows how IBM ILOG Script can be used for this purpose. Note that, typically, not all parameters shown there are used at the same time (e.g., cplex.tilim or cplex.dettilim).

Example 9.10.mod 1 execute { 2 cplex.tilim = 10; 3 cplex.dettilim = 10000; 4 cplex.epgap = 0.1; 5 cplex.epagap = 1; 6}

//CPLEX time limit in seconds //Deterministic time limit in ticks //Relative MIP gap tolerance //Absolute MIP gap tolerance

186

9 Selected functionalities of IBM ILOG Script

Table 9.8 CPLEX parameters

Parameter tilim

Default value 1 E75

dettilim

1 E75

epagap

1 E-6

epgap

1 E-4

Description CPLEX time limit in seconds. Optimization is terminated when this limit is exceeded. Deterministic CPLEX time limit. Optimization is terminated when this limit is exceeded. Absolute MIP gap termination condition: The optimization is terminated as soon as an integer solution is found whose objective function value is at most this absolute distance (epagap) away from the proven optimum. Relative MIP gap termination condition: The optimization is terminated as soon as an integer solution is found whose objective function value is at most this relative distance (epgap) away from the proven optimum.

Table 9.8 lists some important CPLEX parameters that can be set by using IBM ILOG Script. A complete list and detailed description of all parameters can be found in the Studio Help under "List of CPLEX parameters."

9.4

Solution of Case Study 7

Example 9.11.mod shows a solution for the case study described at the beginning of the chapter. First, the CSV files of Example 9.11a.csv and Example 9.11b.csv are read in. In this way, the corresponding input data is initialized. The function loadCSV() of Section 9.1 (Example 9.5.mod) is used for this. That section explained why the quantities products and machines are filled first, and the arrays based on them are filled only afterward by reading the CSV files again. The example also demonstrates how a twodimensional matrix (here: consumption) can be initialized with data from a CSV file. From line 47 onward, the dollar exchange rate is read from the text file "example_9.11.txt". This is then used to convert the contribution margin (in the array cm) from EUR to USD. Decision variables, objective function, and constraints follow. After the optimization (or in this case, after the subject-to block), the optimization results are output. In between and at the end, the current runtime is output by using the function time() from Example 9.8.mod. Finally, it should be noted that Example 9.11.mod’s solution yields exactly the same result as Case Study 3’s solution given in Example 5.11 in Chapter 5 (optimal solution:

9.4 Solution of Case Study 7

187

quantity1 = 6, quantity2 = 200, quantity3 = 10; optimal objective function value: [USD] 37,775). The difference is that all the data is now defined outside the model.

Example 9.11.mod (Solution of Case Study 7) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

include "example_9.08.mod"; //function time() for time measurement include "example_9.05.mod"; //function loadCSV() {string} products = {}; execute { //fill the set products from example_9.11a.csv function func1(array) { products.add(array[0]); } loadCSV("example_9.11a.csv", func1); } {string} machines = {}; execute { //fill the set machines from example_9.11b.csv function func2(array) { machines.add(array[0]); } loadCSV("example_9.11b.csv", func2); } int demand[products]; float cm[products]; execute { //read demand and cm from example_9.11a.csv function func3(array) { demand[array[0]] = array[1]; cm[array[0]] = array[2]; } loadCSV("example_9.11a.csv", func3); } int capacity[machines]; float consumption[machines,products]; execute { //read capacity and resource consumption // from example_9.11b.csv function func4(array) { capacity[array[0]] = array[1]; var i = 2; //starting from column i=2 there is a resource consumption // for each product for (var p in products) { consumption[array[0]][p] = array[i]; i++; } } loadCSV("example_9.11b.csv", func4); }

188

9 Selected functionalities of IBM ILOG Script

46 47 float dollarRate; 48 execute { //read dollar rate from file example_9.11.txt 49 var fileObject = new IloOplInputFile("example_9.11.txt"); 50 dollarRate = parseFloat(fileObject.readline()); 51 writeln(dollarRate); 52 } 53 54 execute { //conversion CM from EUR to USD 55 for (var p in products) { 56 cm[p] = dollarRate*cm[p]; 57 } 58 writeln(time()); 59 } 60 61 dvar int+ quantity[products]; //Planned units to be produced 62 63 maximize sum(p in products) cm[p]*quantity[p]; 64 65 subject to { 66 forall(m in machines) 67 sum(p in products) consumption[m,p]*quantity[p] = 1; //cf. (11.2) as >= condition forall(i,j in districts){ x[i][j] = >=