| approx.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file approx.mos
   ```````````````
   Function approximation with SOS2   
   SOS2s are generally used for modeling piecewise approximations
   of functions of a single variable. In this example, we aim 
   to represent f as a function of x and we consider four line
   segments between five points. The five points are (R1, F1), 
   (R2, F2), (R3, F3), (R4, F4) and (R5, F5) and associated with 
   each point i is a weight variable y(i). Binary variable b(i) is
   associated with each of the intervals (R1, R2), (R2, R3), (R3, 
   R4) and (R4, R5), so 'b(i)' takes value 1 if the value of x 
   lies between 'R(i)' and 'R(i+1)'.
   The SOS2 property of having at most two non-zero 'y(i)', and 
   if there are two non-zero then they must be adjacent, implies 
   that we are always on the piece-wise linear function.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Sep. 2006
*******************************************************!)
model "Approximation"
 uses "mmxprs"
 declarations
  NB = 5
  BREAKS = 1..NB
  R,F: array(BREAKS) of real           ! Coordinates of break points
  x,f: mpvar                           ! Decision variables
  y: array(BREAKS) of mpvar            ! Weight variables
 end-declarations
 R:: [1, 2.2, 3.4, 4.8, 6.5]
 F:: [2, 3.2, 1.4, 2.5, 0.8]
 
 RefRow:= sum(i in BREAKS) R(i)*y(i) 
 x = RefRow
 f = sum(i in BREAKS) F(i)*y(i)
 
! Convexity constraint
 sum(i in BREAKS) y(i) = 1
! SOS2 definition
 RefRow is_sos2
! Alternative SOS definition (to be used for 0-valued RefRow coefficients):
! makesos2(union(i in BREAKS) {y(i)}, RefRow)
(! Alternative formulation using binaries instead of SOS2:
 declarations
  b: array(1..NB-1) of mpvar
 end-declarations
 sum(i in 1..NB-1) b(i) = 1
 forall(i in 1..NB-1) b(i) is_binary
 forall(i in BREAKS) y(i) <= if(i>1, b(i-1), 0) + if(i<NB, b(i), 0)
!)
! Bounds 
 1<=x; x<=6.5
! Solve the problem
 minimize(f)
 
 writeln("Objective value: ", getobjval)
 writeln("x: ", getsol(x))
  
end-model 
 | 
| 
 | 
| approx2.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file approx2.mos
   ````````````````
   Function approximation via piecewise linear expression   
   In this example, we aim to represent f as a function of x 
   that is specified via four line segments between the five 
   points (X(i),FX(i)).  
   We use the 'pwlin' construct to state f as a piecewise linear
   constraint that will be handled directly by the MIP solver
   although the constraint formulation requires the nonlinear
   module 'mmxnlp'.
   (c) 2021 Fair Isaac Corporation
       author: S. Heipcke, July 2021
*******************************************************!)
model "Approximation (pwlin)"
 uses "mmxnlp"
 declarations
  NB = 5
  BREAKS = 1..NB
  X,FX: array(BREAKS) of real          ! Coordinates of break points
  x,f: mpvar                           ! Decision variables
 end-declarations
 X::  [1, 2.2, 3.4, 4.8, 6.5]
 FX:: [2, 3.2, 1.4, 2.5, 0.8]
 f = pwlin(x, union(i in BREAKS) [X(i),FX(i)])
 
! Bounds 
 1<=x; x<=6.5
! Solve the problem
 minimize(f)
 
 writeln("Objective value: ", getobjval)
 writeln("x: ", getsol(x))
  
end-model 
 | 
| 
 | 
| burglar1.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file burglar1.mos
   `````````````````
   Knapsack problem   
   A burglar considers eight items that have different values
   and weights. He wants to take a group of items that maximizes
   the total value while the total weight is not more than the
   maximum 'WTMAX' he can carry.
   This IP model represents the so-called knapsack problem where 
   binary decision variable 'take(i)' takes value 1 if item 'i'
   is taken; 0 otherwise.
   (c) 2008 Fair Isaac Corporation
       author: R.C. Daniel, Jul. 2002
*******************************************************!)
model "Burglar 1" 
 uses "mmxprs"
  
 declarations
  ITEMS = 1..8                   ! Index range for items
  WTMAX = 102                    ! Maximum weight allowed
  
  VALUE: array(ITEMS) of real    ! Value of items
  WEIGHT: array(ITEMS) of real   ! Weight of items
  
  take: array(ITEMS) of mpvar    ! 1 if we take item i; 0 otherwise
 end-declarations
! Item:      1    2  3   4   5   6   7   8
  VALUE :: [15, 100, 90, 60, 40, 15, 10, 1]
  WEIGHT:: [ 2,  20, 20, 30, 40, 30, 60, 10]
! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) VALUE(i)*take(i) 
! Weight restriction
 sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX
! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary  
 maximize(MaxVal)                 ! Solve the MIP-problem
! Print out the solution
 writeln("Solution:\n Objective: ", getobjval)
 forall(i in ITEMS)  writeln(" take(", i, "): ", getsol(take(i)))
end-model
 | 
| 
 | 
| burglar2.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file burglar2.mos
   `````````````````
   Knapsack problem   
  
   A burglar considers eight items that have different values
   and weights. He wants to take a group of items that maximizes
   the total value while the total weight is not more than the
   maximum 'WTMAX' he can carry.
   This IP model represents the so-called knapsack problem where
   binary decision variable 'take(i)' takes value 1 if item 'i'
   is taken; 0 otherwise. This implementation illustrates how 
   problem data could be read into tables from text files. 
   Instead of having the item data embedded in the Mosel model 
   file, we have the data in a file and use 'initializations from'.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Aug. 2002
*******************************************************!)
model "Burglar 2" 
 uses "mmxprs"
 declarations
  ITEMS: set of string           ! Set of items
  WTMAX = 102                    ! Maximum weight allowed
  VALUE: array(ITEMS) of real    ! Value of items
  WEIGHT: array(ITEMS) of real   ! Weight of items
 end-declarations
 initializations from 'burglar.dat'
  VALUE
  WEIGHT
 end-initializations
(! alternatively:
 initializations from 'burglar2.dat'
  [VALUE, WEIGHT] as 'KNAPSACK'
 end-initializations
!)
 declarations
  take: array(ITEMS) of mpvar    ! 1 if we take item i; 0 otherwise
 end-declarations
! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) VALUE(i)*take(i) 
! Weight restriction
 sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX
! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary  
 maximize(MaxVal)                 ! Solve the MIP-problem
! Print out the solution
 writeln("Solution:\n Objective: ", getobjval)
 forall(i in ITEMS)  writeln(" take(", i, "): ", getsol(take(i)))
end-model
 | 
| 
 | 
| burglari.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file burglari.mos
   `````````````````
   Knapsack problem
   -- Using string indices --   
  -- Using string indices --
   A burglar considers eight items that have different values
   and weights. He wants to take a group of items that maximizes
   the total value while the total weight is not more than the
   maximum 'WTMAX' he can carry.
   This IP model represents the so-called knapsack problem where
   binary decision variable 'take(i)' takes value 1 if item 'i'
   is taken; 0 otherwise. This implementation illustrates the
   use of string indices having the data embedded in the model file.
   (c) 2008 Fair Isaac Corporation
       author: R.C. Daniel, Jul. 2002
*******************************************************!)
model "Burglar 1 (index set)" 
 uses "mmxprs"
  
 declarations
  ITEMS = {"camera", "necklace", "vase", "picture", "tv", "video", 
           "chest", "brick"}     ! Set for items
  WTMAX = 102                    ! Maximum weight allowed
  
  VALUE: array(ITEMS) of real    ! Value of items
  WEIGHT: array(ITEMS) of real   ! Weight of items
  
  take: array(ITEMS) of mpvar    ! 1 if we take item i; 0 otherwise
 end-declarations
 VALUE("camera")  := 15;  WEIGHT("camera")  :=  2
 VALUE("necklace"):=100;  WEIGHT("necklace"):= 20
 VALUE("vase")    := 90;  WEIGHT("vase")    := 20
 VALUE("picture") := 60;  WEIGHT("picture") := 30
 VALUE("tv")      := 40;  WEIGHT("tv")      := 40
 VALUE("video")   := 15;  WEIGHT("video")   := 30
 VALUE("chest")   := 10;  WEIGHT("chest")   := 60
 VALUE("brick")   :=  1;  WEIGHT("brick")   := 10
(! Alternative data initialization:
  VALUE :: (["camera", "necklace", "vase", "picture", "tv", "video", 
             "chest", "brick"])[15, 100, 90, 60, 40, 15, 10, 1]
  WEIGHT:: (["camera", "necklace", "vase", "picture", "tv", "video", 
             "chest", "brick"])[ 2,  20, 20, 30, 40, 30, 60, 10]
!)
! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) VALUE(i)*take(i) 
! Weight restriction
 sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX
! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary  
 maximize(MaxVal)                 ! Solve the MIP-problem
! Print out the solution
 writeln("Solution:\n Objective: ", getobjval)
 forall(i in ITEMS)  writeln(" take(", i, "): ", getsol(take(i)))
end-model
 | 
| 
 | 
| burglar_rec.mos | 
| (!******************************************************
   Mosel Example Problems
   ====================== 
   file burglar_rec.mos
   ````````````````````
   Use of records.
   
   A burglar considers eight items that have different values
   and weights. He wants to take a group of items that maximizes
   the total value while the total weight is not more than the
   maximum 'WTMAX' he can carry.
   This IP model represents the so-called knapsack problem where 
   binary decision variable 'take(i)' takes value 1 if item 'i'
   is taken; 0 otherwise. This implementation illustrates that 
   data may also be given in the form of a single record, in this
   case the array 'I' which is populated with the data file 
   burglar_rec.dat.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, July 2006
*******************************************************!)
model "Burglar (records)"
 uses "mmxprs"
 
 declarations
  WTMAX = 102                    ! Maximum weight allowed
  ITEMS = {"camera", "necklace", "vase", "picture", "tv", "video", 
           "chest", "brick"}     ! Index set for items
  
  I: array(ITEMS) of record
   VALUE: real                   ! Value of items
   WEIGHT: real                  ! Weight of items
  end-record
  take: array(ITEMS) of mpvar    ! 1 if we take item i; 0 otherwise
 end-declarations
 initializations from 'burglar_rec.dat'
  I
 end-initializations
! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) I(i).VALUE*take(i) 
! Weight restriction
 sum(i in ITEMS) I(i).WEIGHT*take(i) <= WTMAX
! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary  
 maximize(MaxVal)                 ! Solve the MIP-problem
! Print out the solution
 writeln("Solution:\n Objective: ", getobjval)
 forall(i in ITEMS)  writeln(" take(", i, "): ", getsol(take(i)))
end-model
 | 
| 
 | 
| chess.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file chess.mos
   ``````````````
   Production of chess boards   
   A small company manufactures two different sizes of boxwood
   chess sets. The small set requires 3 lathehours and 1 kg 
   of boxwood. The large set requires 2 lathehours and 3 kg 
   of boxwood. There are 160 lathe-hours and 200 kg available  
   per week. Each large (resp. small) chess set produced and 
   sold yields a profit of $20 (resp. $5). How many sets of 
   each kind should be made each week to maximize total profit?
   
   A simple LP model is implemented. After optimization, 
   'getobjval' returns the optimal objective function value 
   while 'getsol' returns the decision variables optimal values. 
   (c) 2008 Fair Isaac Corporation
       author: R.C. Daniel, Jul. 2002
*******************************************************!)
model Chess
 uses "mmxprs"
 
 declarations
  xs, xl: mpvar                   ! Decision variables: produced quantities
 end-declarations
 Profit:=  5*xs + 20*xl           ! Objective function
 Boxwood:= 1*xs + 3*xl <=  200    ! kg of boxwood
 Lathe:=   3*xs + 2*xl <=  160    ! Lathehours
 
 maximize(Profit)                 ! Solve the problem
 writeln("LP Solution:")          ! Solution printing
 writeln(" Objective: ", getobjval)
 writeln("Make ", getsol(xs), " small sets")
 writeln("Make ", getsol(xl), " large sets")
end-model
 | 
| 
 | 
| chess2.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file chess2.mos
   ```````````````
   Production of chess boards   
  
   A small company manufactures two different sizes of boxwood
   chess sets. The small set requires 3 lathehours and 1 kg 
   of boxwood. The large set requires 2 lathehours and 3 kg 
   of boxwood. There are 160 lathe-hours and 200 kg available  
   per week. Each large (resp. small) chess set produced and 
   sold yields a profit of $20 (resp. $5). How many sets of 
   each kind should be made each week to maximize total profit?
  
   Analyzing the problem solution further, 'getact' returns 
   the constraint activities, 'getdual' their dual values 
   (shadow prices), and 'getrcost' returns the reduced costs 
   of the decision variables.
   (c) 2008 Fair Isaac Corporation
       author: R.C. Daniel, Jul. 2002
*******************************************************!)
model "Chess 2"
 uses "mmxprs"
 
 declarations
  xs, xl: mpvar                   ! Decision variables: produced quantities
 end-declarations
 Profit:=  5*xs + 20*xl           ! Objective function
 Boxwood:= 1*xs + 3*xl <=  200    ! kg of boxwood
 Lathe:=   3*xs + 2*xl <=  160    ! Lathehours
 
 maximize(Profit)                 ! Solve the problem
 writeln("LP Solution:")          ! Solution printing
 writeln(" Objective: ", getobjval)
 writeln("Make ", getsol(xs), " small sets")
 writeln("Make ", getsol(xl), " large sets")
 writeln("Activities/Dual Values")
 writeln(" Lathe: ", getact(Lathe)," / ", getdual(Lathe))
 writeln(" Boxwood: ", getact(Boxwood)," / ", getdual(Boxwood))
 writeln("Reduced Costs")
 writeln(" xs: ", getrcost(xs))
 writeln(" xl: ", getrcost(xl))
end-model
 | 
| 
 | 
| pricebrai.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file pricebrai.mos
   ``````````````````
   Modeling price breaks:
   All item discount pricing   
   
   This implementation represents the situation where we 
   buy a certain number of items and we get discounts on 
   all items that we buy if the quantity we buy lies in 
   certain price bands. We have three price bands. If we 
   buy a number of items in [0,B1) then for each item we 
   pay COST1, if the quantity we buy lies in [B1,B2) we 
   pay COST2 for each item. Finally if we buy an amount 
   in [B2,B3) then we pay an amount COST3 for each item. 
   This sort of pricing is called all item discount pricing.
   
   To model these item price breaks we use binary variables 
   b(i) which takes value 1 if we pay a unit cost of COST(i).
   We also define continuous decision variables x1, x2 and 
   x3 which represent the number of items bought at price 
   COST1, COST2, and COST3, respectively.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Sep. 2006
*******************************************************!)
model "All item discount"
 uses "mmxprs"
 
 declarations
  NB = 3                                ! Number of price bands
  BREAKS = 1..NB
  COST: array(BREAKS) of real           ! Cost per unit
  x: array(BREAKS) of mpvar             ! Number of items bought at a price
  b: array(BREAKS) of mpvar             ! Indicators of price bands
  B: array(0..NB) of real               ! Break points of cost function
 end-declarations
 
 DEM:= 150                              ! Demand
 B:: [0, 50, 120, 200]
 COST:: [ 1, 0.7, 0.5]
 
 forall(i in BREAKS) b(i) is_binary
(! Alternatively
 sum(i in BREAKS) B(i)*b(i) is_sos1
!) 
! Objective: total price
 TotalCost:= sum(i in BREAKS) COST(i)*x(i)  
! Meet the demand
 sum(i in BREAKS) x(i) = DEM 
! Lower and upper bounds on quantities
 forall(i in BREAKS) do
  B(i-1)*b(i) <= x(i);  x(i) <= B(i)*b(i)
 end-do
! The quantity bought lies in exactly one interval
 sum(i in BREAKS) b(i) = 1
! Solve the problem
 minimize(TotalCost)
! Solution printing
 writeln("Objective: ", getobjval, 
         " (price per unit: ", getobjval/DEM, ")")
 forall(i in BREAKS) 
  writeln("Interval ", i, ": ", getsol(x(i)), " (price per unit: ", COST(i), ")")
end-model
 | 
| 
 | 
| pricebrai2.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file pricebrai2.mos
   ```````````````````
   Modeling price breaks:
   All item discount pricing
   - formulation as piecewise linear expression -
   
   This implementation represents the situation where we 
   buy a certain number of items and we get discounts on 
   all items that we buy if the quantity we buy lies in 
   certain price bands. We have three price bands. If we 
   buy a number of items in [0,B1) then for each item we 
   pay COST1, if the quantity we buy lies in [B1,B2) we 
   pay COST2 for each item. Finally if we buy an amount 
   in [B2,B3) then we pay an amount COST3 for each item. 
   This sort of pricing is called all item discount pricing.
   
   To model these item price breaks we use a 'pwlin' 
   expression specified via a list of the linear segments.
   This general constraint is handled directly by the MIP solver
   although the constraint formulation requires the nonlinear
   module 'mmxnlp'.
  
   (c) 2021 Fair Isaac Corporation
       author: S. Heipcke, July 2021
*******************************************************!)
model "All item discount (pwlin)"
 uses "mmxnlp"
 
 declarations
  NB = 3                                ! Number of price bands
  BREAKS = 1..NB
  COST: array(BREAKS) of real           ! Cost per unit
  x: mpvar                              ! Number of items bought  
  B: array(0..NB) of real               ! Break points of cost function
 end-declarations
 
 DEM:= 150                              ! Demand
 B:: [0, 50, 120, 200]
 COST:: [ 1, 0.7, 0.5]
 ! Objective: Specification as list of piecewise linear segments
 TotalCost:= pwlin(x, union(i in 1..3) [B(i-1), COST(i)*B(i-1), B(i), COST(i)*B(i)])
 ! Objective: Specification as list of points
 ! TotalCost:= pwlin(x, union(i in 1..3) [B(i-1), COST(i)*B(i-1), B(i), COST(i)*B(i)])
! Meet the demand
 x = DEM 
! Solve the problem
 minimize(TotalCost)
! Solution printing
 writeln("Objective: ", getobjval, 
         " (price per unit: ", getobjval/DEM, ")")
 forall(i in BREAKS) 
  writeln("Interval ", i, ": ", if(x.sol>=B(i-1) and x.sol<=B(i),x.sol,0), 
    " (price per unit: ", COST(i), ")")
end-model
 | 
| 
 | 
| pricebrinc.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file pricebrinc.mos
   ```````````````````
   Incremental pricebreaks formulated with SOS2   
  
   This model represents the situation where we buy a certain
   number of items and we get discounts incrementally. The 
   unit cost for items between 0 and B1 is C1, whereas items
   between B1 and B2 cost C2 each, and items between B2 and
   B3 cost C3 each.
   
   This implementation uses Special Ordered Sets of type 2 
   (SOS2). At the points 0, B1, B2 and B3, we introduce continuous 
   decision variables 'w(i)' (i = 0, 1, 2, 3). We also define cost 
   break points 'CBP(i)' that correspond to the total cost of buying 
   quantities 0, B1, B2 and B3. Then, 'w(i)' defines a SOS2 with 
   reference row coefficients given by the coefficients in the 
   definition of x. In this example, we use 'makesos2' to define 
   the SOS2.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Sep. 2006
*******************************************************!)
model "Incremental pricebreaks (SOS2)"
 uses "mmxprs"
 
 declarations
  NB = 3                                ! Number of price bands
  BREAKS = 0..NB
  COST: array(1..NB) of real            ! Cost per unit within price bands
  w: array(BREAKS) of mpvar             ! Weight variables
  x: mpvar                              ! Total quantity bought
  B,CBP: array(BREAKS) of real          ! Break points, cost break points
 end-declarations
 
 DEM:= 150                              ! Demand
 B::  [0, 50, 120, 200]
 COST:: [0.8, 0.5, 0.3]
 
 CBP(0):= 0
 forall(i in 1..NB) CBP(i):= CBP(i-1) + COST(i) * (B(i)-B(i-1))
! Objective: total price
 TotalCost:= sum(i in BREAKS) CBP(i)*w(i)  
! Meet the demand
 x = DEM 
! Definition of x
 Defx:= x = sum(i in BREAKS) B(i)*w(i)
! Weights sum up to 1
 sum(i in BREAKS) w(i) = 1
! Definition of SOS2 
! (we cannot use 'is_sos2' since there is a 0-valued coefficient)
 makesos2(union(i in BREAKS) {w(i)}, Defx)
! Solve the problem
 minimize(TotalCost)
! Solution printing
 writeln("Objective: ", getobjval, 
         " (avg price per unit: ", getobjval/DEM, ")")
 forall(i in BREAKS)
  writeln("w(", i, "): ", getsol(w(i)), " (price per unit: ", 
   if(i>0, CBP(i)/B(i), CBP(i)), ")")
end-model
 | 
| 
 | 
| pricebrinc2.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file pricebrinc2.mos
   ````````````````````
   Incremental pricebreaks formulated with 
   binary variables   
   This model represents the situation where we buy a certain
   number of items and we get discounts incrementally. The
   unit cost for items between 0 and B1 is C1, whereas items
   between B1 and B2 cost C2 each, and items between B2 and
   B3 cost C3 each.
   We model the incremental price breaks by using binary
   decision variables. Binary variable 'b(i)' takes value 1
   if we have bought any items at a unit cost of 'COST(i)'.
   Decision variables 'x(i)' is the number of items bought at
   price 'COST(i)'.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Sep. 2006
*******************************************************!)
model "Incremental pricebreaks (binaries)"
 uses "mmxprs"
 
 declarations
  NB = 3                                ! Number of price bands
  BREAKS = 1..NB
  COST: array(BREAKS) of real           ! Cost per unit
  x: array(BREAKS) of mpvar             ! Number of items bought at a price
  b: array(BREAKS) of mpvar             ! Indicators of price bands
  B: array(0..NB) of real               ! Break points of cost function 
 end-declarations
 
 DEM:= 150                              ! Demand
 B::  [0, 50, 120, 200]
 COST:: [0.8, 0.5, 0.3]
 
 forall(i in BREAKS) b(i) is_binary
! Objective: total price
 TotalCost:= sum(i in BREAKS) COST(i)*x(i)  
! Meet the demand
 sum(i in BREAKS) x(i) = DEM 
! Lower and upper bounds on quantities
 forall(i in 1..NB-1) (B(i)-B(i-1)) * b(i+1) <= x(i)
 forall(i in BREAKS)  x(i) <= (B(i)-B(i-1)) * b(i)
! Sequence of price intervals
 forall(i in 1..NB-1) b(i) >= b(i+1)
! Solve the problem
 minimize(TotalCost)
! Solution printing
 writeln("Objective: ", getobjval, 
         " (avg price per unit: ", getobjval/DEM, ")")
 forall(i in BREAKS) 
  writeln("x(", i, "): ", getsol(x(i)), " (price per unit: ", COST(i), ")")
end-model
 | 
| 
 | 
| pricebrinc3.mos | 
| (!******************************************************
   Mosel Example Problems
   ======================
   file pricebrinc3.mos
   ````````````````````
   Incremental pricebreaks formulated with 
   piecewise linear expressions
   This model represents the situation where we buy a certain
   number of items and we get discounts incrementally. The
   unit cost for items between 0 and B1 is C1, whereas items
   between B1 and B2 cost C2 each, and items between B2 and
   B3 cost C3 each.
   We model the incremental price breaks via 'pwlin' expressions. 
   Three different versions can be used for representing 
   this particular case of a continuous cost function.
   This general constraint is handled directly by the MIP solver
   although the constraint formulation requires the nonlinear
   module 'mmxnlp'.
   (c) 2021 Fair Isaac Corporation
       author: S. Heipcke, July 2021
*******************************************************!)
model "Incremental pricebreaks (pwlin)"
 uses "mmxnlp"
 
 declarations
  NB = 3                                ! Number of price bands
  BREAKS = 0..NB
  COST: array(1..NB) of real            ! Cost per unit within price bands
  x: mpvar                              ! Total quantity bought
  B,CBP: array(BREAKS) of real          ! Break points, cost break points
 end-declarations
 
 DEM:= 150                              ! Demand
 B::  [0, 50, 120, 200]
 COST:: [0.8, 0.5, 0.3]
 
 CBP(0):= 0
 forall(i in 1..NB) CBP(i):= CBP(i-1) + COST(i) * (B(i)-B(i-1))
! Objective: total price (uncomment one of the three following options)
! 1- Specification as a list of slopes with associated intervals:
! (only points of slope changes are specified, the start value is 0):
!TotalCost:= pwlin(x, union(i in 1..2) [B(i)], union(i in 1..3) [COST(i)])
! 2- Specification as a list of piecewise linear segments (pws) with associated intervals:
TotalCost:= pwlin(union(i in 1..3) [pws(B(i-1), CBP(i-1)+COST(i)*(x-B(i-1)))])
! 3- Specification as a list of points:
!TotalCost:= pwlin(x, union(i in 0..3) [B(i), CBP(i)])
! Meet the demand
 x = DEM 
! Solve the problem
 minimize(TotalCost)
! Solution printing
 writeln("Objective: ", getobjval, 
         " (avg price per unit: ", getobjval/DEM, ")")
 forall(i in 1..NB)
  writeln("Interval ", i, ": ", minlist(B(i)-B(i-1),x.sol-B(i-1)), " (price per unit: ", COST(i), ")")
end-model
 | 
| 
 |