FICO Xpress Optimization Examples Repository
 FICO Optimization Community FICO Xpress Optimization Home

Description
There are three suppliers of a good, and they have quoted various prices for various quantities of product. We want to buy at least total cost, yet not buy too much from any one supplier. Each supplier offers decreasing prices for increased lot size, in the form of incremental discounts. We wish to buy 600 items in total.
• complex MIP model
• data input from file, including parameters for dimensioning
• splitting the declaration section to read first the parameters that are used to declare the data arrays
• using SOS-2
Further explanation of this example: 'Xpress teaching material', Section 2.4 'SOS-2: Purchasing with price breaks'; 'Applications of optimization with Xpress-MP', Section 3.4.5 'Special Ordered Sets of type 2'

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.

Data Files

purchase_graph.mos

(!******************************************************
Mosel Example Problems
======================

file purchase.mos

DIFFICULTY:   3
FEATURES:     MIP problem, modeling a piecewise linear function with SOS-2,
graphical representation of data
DESCRIPTION:  There are three suppliers of a good, and they have quoted
various prices for various quantities of product. We want
to buy at least total cost, yet not buy too much from any
one supplier. Each supplier offers decreasing prices for
increased lot size, in the form of incremental discounts.
We wish to buy 600 items in total.
FURTHER INFO: Applications of optimization with Xpress-MP teaching
material', Section 2.4 SOS-2: Purchasing with price breaks';
Applications of optimization with Xpress-MP',
Section 3.4.5 Special Ordered Sets of type 2'

(c) 2008 Fair Isaac Corporation
author: S. Heipcke, Jan. 2001, rev. July 2020
*******************************************************!)

model "Purchase"
uses "mmxprs", "mmsvg"

declarations
NB = 3
BREAK1 = 1..NB                     ! Price breaks
BREAK0 = 0..NB                     ! Price breaks including 0
SUPPL = 1..3                       ! Suppliers

COST: array(SUPPL,BREAK1) of real  ! Unit cost
B: array(SUPPL,BREAK0) of real     ! Breakpoints (quantities at which unit
! cost changes)
CB: array(SUPPL,BREAK0) of real    ! Total cost at break points
MAXPERC: array(SUPPL) of real      ! Maximum percentages for each supplier
REQ: real                          ! Total quantity required

buy: array(SUPPL) of mpvar         ! Quantity to purchase from supplier s
w: array(SUPPL,BREAK0) of mpvar    ! Weights at breakpoint k for supplier s
end-declarations

initializations from 'purchase.dat'
COST B MAXPERC REQ
end-initializations

! Graphical representation of data
declarations
SupplGraph: array(SUPPL) of integer
COLOR: array(SUPPL) of integer
end-declarations

FACT:=100
svgsetgraphviewbox(0, 0, max(s in SUPPL) B(s,NB), FACT*max(s in SUPPL,b in BREAK1) COST(s,b)+1)
svgsetgraphlabels("Units","Cost per unit")
svgsetgraphstyle(SVG_STROKEWIDTH,5)
forall(s in SUPPL) do
svgaddline(sum(b in BREAK1) [B(s,b-1), FACT*COST(s,b), B(s,b), FACT*COST(s,b)])
end-do

! Calculate total cost at breakpoints
forall(s in SUPPL) do
CB(s,0):= 0
forall(b in BREAK1) CB(s,b):= CB(s,b-1) + COST(s,b)*(B(s,b)-B(s,b-1))
end-do

! Objective: sum of costs*weights
MinCost:= sum(s in SUPPL, b in BREAK0) CB(s,b) * w(s,b)

! Define buy and also order the weight variables by breakpoint quantities
forall(s in SUPPL) Buy(s):= sum(b in BREAK0) B(s,b) * w(s,b) = buy(s)

! The convexity row (w sum to 1)
forall(s in SUPPL) OneW(s):= sum(b in BREAK0) w(s,b) = 1

! The minimum quantity that must be bought
Demand:= sum(s in SUPPL) buy(s) >= REQ

! No more than the maximum percentage from each supplier
forall(s in SUPPL) buy(s) <= MAXPERC(s)*REQ/100.0

! Define the w as SOS-2 as we can linearly interpolate between the
! breakpoints.
forall(s in SUPPL)
makesos2(union(b in BREAK0) {w(s,b)}, sum(b in BREAK0) B(s,b)*w(s,b))

! Alternative formulation:
!   The weight coefficients B are all augmented by EPS
!   since Mosel does not accept 0-valued weights with is_sos2'.
! forall(s in SUPPL) sum(b in BREAK0) (B(s,b)+10E-20) * w(s,b) is_sos2

minimize(MinCost)              ! Solve the MIP-problem

writeln("Minimum cost: ", getobjval)