Initializing help system before first use

Introductory examples


Description:

Problem name and type, features Difficulty
approx Approximation: Piecewise linear approximation **
SOS-2, Special Ordered Sets, piecewise linear approximation of a nonlinear function, pwlin
burglar MIP modeling: Knapsack problem: 'Burglar' *
simple MIP model with binary variables, data input from text data file, array initialization, numerical indices, string indices, record data structure
chess LP modeling: Production planning: 'Chess' problem *
simple LP model, solution output, primal solution values, slack values, activity values, dual solution values
pricebrai All item discount pricing: Piecewise linear function ***
SOS-1, Special Ordered Sets, piecewise linear function, approximation of non-continuous function, step function, pwlin
pricebrinc Incremental pricebreaks: Piecewise linear function ***
SOS-2, Special Ordered Sets, piecewise linear function, step function


File(s): approx.mos (Sep. 2006), approx2.mos (Jul. 2021), burglar1.mos (Jul. 2002), burglar2.mos (Aug. 2002), burglari.mos (Jul. 2002), burglar_rec.mos (Jul. 2006), chess.mos (Jul. 2002), chess2.mos (Jul. 2002), pricebrai.mos (Sep. 2006), pricebrai2.mos (Jul. 2021), pricebrinc.mos (Sep. 2006), pricebrinc2.mos (Sep. 2006), pricebrinc3.mos (Jul. 2021)
Data file(s): burglar.dat, burglar2.dat, burglar_rec.dat

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, rev. Sep. 2024
*******************************************************!)

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
  TotalCost: nlctr
 end-declarations
 
 DEM:= 150                              ! Demand
 B:: [0, 50, 120, 200]
 COST:: [ 1, 0.7, 0.5]

 ! Objective: 1- Specification as list of piecewise linear segments
 TotalCost:= pwlin(union(i in 1..3) [pws(B(i-1), COST(i)*x)])

 ! Objective: 2- 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

(! Uncomment to display the problem definition 
 loadprob(TotalCost)
 writeprob("","l")
!)
! 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

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.