A transport example
A company produces the same product at different plants in the UK. Every plant has a different production cost per unit and a limited total capacity. The customers (grouped into customer regions) may receive the product from different production locations. The transport cost is proportional to the distance between plants and customers, and the capacity on every delivery route is limited. The objective is to minimize the total cost, whilst satisfying the demands of all customers.
Model formulation
Let PLANT be the set of plants and REGION the set of customer regions. We define decision variables flowpr for the quantity transported from plant p to customer region r. The total cost of the amount of product p delivered to region r is given as the sum of the transport cost (the distance between p and r multiplied by a factor FUELCOST) and the production cost at plant p:
∑ |
p ∈ PLANT |
∑ |
r ∈ REGION |
The limits on plant capacity are give through the constraints
∑ |
r ∈ REGION |
We want to meet all customer demands:
∑ |
p ∈ PLANT |
The transport capacities on all routes are limited and there are no negative flows:
For simplicity's sake, in this mathematical model we assume that all routes p → r are defined and that we have TRANSCAPpr=0 to indicate that a route cannot be used.
Implementation
This problem may be implemented with Mosel as shown in the following (model file transport.mos):
model Transport uses "mmxprs" declarations REGION: set of string ! Set of customer regions PLANT: set of string ! Set of plants DEMAND: array(REGION) of real ! Demand at regions PLANTCAP: array(PLANT) of real ! Production capacity at plants PLANTCOST: array(PLANT) of real ! Unit production cost at plants TRANSCAP: dynamic array(PLANT,REGION) of real ! Capacity on each route plant->region DISTANCE: dynamic array(PLANT,REGION) of real ! Distance of each route plant->region FUELCOST: real ! Fuel cost per unit distance flow: dynamic array(PLANT,REGION) of mpvar ! Flow on each route end-declarations initializations from 'transprt.dat' DEMAND [PLANTCAP,PLANTCOST] as 'PLANTDATA' [DISTANCE,TRANSCAP] as 'ROUTES' FUELCOST end-initializations ! Create the flow variables that exist forall(p in PLANT, r in REGION | exists(TRANSCAP(p,r)) ) create(flow(p,r)) ! Objective: minimize total cost MinCost:= sum(p in PLANT, r in REGION | exists(flow(p,r))) (FUELCOST * DISTANCE(p,r) + PLANTCOST(p)) * flow(p,r) ! Limits on plant capacity forall(p in PLANT) sum(r in REGION) flow(p,r) <= PLANTCAP(p)</p> ! Satisfy all demands forall(r in REGION) sum(p in PLANT) flow(p,r) = DEMAND(r) ! Bounds on flows forall(p in PLANT, r in REGION | exists(flow(p,r))) flow(p,r) <= TRANSCAP(p,r) minimize(MinCost) ! Solve the problem end-model
REGION and PLANT are declared to be sets of strings, as yet of unknown size. The data arrays (DEMAND, PLANTCAP, PLANTCOST, TRANSCAP, and DISTANCE) and the array of variables flow are indexed by members of REGION and PLANT, their size is therefore not known at their declaration. The model shows two forms of such array declarations: (1) the arrays DEMAND, PLANTCAP, PLANTCOST are dense arrays that are not fixed (all entries corresponding to their index sets exist, new entries are added via assignment or if their index sets grow), (2) the arrays TRANSCAP, DISTANCE), and flow are marked as dynamic, that is, only explicitly assigned or created entries exist — we want to make use of this property in the formulation of the model.
There is a slight difference between dynamic arrays of data and of decision variables (type mpvar): an entry of a data array is created automatically when it is used in the Mosel program, entries of decision variable arrays need to be created explicitly (see Section Conditional variable creation and create below).
The data file transprt.dat contains the problem specific data. It might have, for instance,
DEMAND: [ (Scotland) 2840 (North) 2800 (SWest) 2600 (SEast) 2820 (Midlands) 2750] ! [CAP COST] PLANTDATA: [ (Corby) [3000 1700] (Deeside) [2700 1600] (Glasgow) [4500 2000] (Oxford) [4000 2100] ] ! [DIST CAP] ROUTES: [ (Corby North) [400 1000] (Corby SWest) [400 1000] (Corby SEast) [300 1000] (Corby Midlands) [100 2000] (Deeside Scotland) [500 1000] (Deeside North) [200 2000] (Deeside SWest) [200 1000] (Deeside SEast) [200 1000] (Deeside Midlands) [400 300] (Glasgow Scotland) [200 3000] (Glasgow North) [400 2000] (Glasgow SWest) [500 1000] (Glasgow SEast) [900 200] (Oxford Scotland) [800 *] (Oxford North) [600 2000] (Oxford SWest) [300 2000] (Oxford SEast) [200 2000] (Oxford Midlands) [400 500] ] FUELCOST: 17
where we give the ROUTES data only for possible plant/region routes, indexed by the plant and region. It is possible that some data are not specified; for instance, there is no Corby – Scotland route. So the data are sparse and we just create the flow variables for the routes that exist. (The `*' for the (Oxford,Scotland) entry in the capacity column indicates that the entry does not exist; we may write '0' instead: in this case the corresponding flow variable will be created but bounded to be 0 by the transport capacity limit).
The condition whether an entry in a data table is defined is tested with the Mosel function exists. With the help of the `|' operator we add this test to the forall loop creating the variables. It is not required to add this test to the sums over these variables: only the flowpr variables that have been created are taken into account. However, if the sums involve exactly the index sets that have been used in the declaration of the variables (here this is the case for the objective function MinCost), adding the existence test helps to speed up the enumeration of the existing index-tuples. The following section introduces the conditional generation in a more systematic way.
© 2001-2019 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.