Initializing help system before first use

Dantzig-Wolfe decomposition: combining sequential and parallel solving


Type: Production planning
Rating: 5 (difficult)
Description: Dantzig-Wolfe decomposition is a method for solving large LP problems. The model implementation shows the following features:
  • iterative sequence of concurrent solving of a set of subproblems
  • data exchange between several models via shared memory
  • coordination of several models via events
Two different implementations are proposed. The first one runs on a single node allowing to execute multiple concurrent solves (cocoMs.mos and cocoSubFs.mos) and the second one executes on multiple nodes (cocoMd.mos and cocoSubFd.mos). Extending the single node model to support distributed architecture requires only few lines of new code to setup the list of nodes and define which node solves which subproblem.
File(s): coco3.mos, cocoMs.mos, cocoSubFs.mos
Data file(s): coco2.dat, coco3.dat


coco3.mos
(!*******************************************************
   Mosel Example Problems 
   ======================

   file coco3.mos
   ``````````````
   Coco Problem, mid-term model.

   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, r2001, ev. Apr. 2005
*******************************************************!)

model "Coco3"
 uses "mmxprs"

 parameters
  DATAFILE = "coco2.dat"
 end-parameters
 
 declarations                         
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      

 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations

 declarations
  PRODS = 1..NPROD                      ! Range of products (p)
  FACT = 1..NFACT                       !          factories (f)
  RAW = 1..NRAW                         !          raw materials (r)
  TIME = 1..NT                          !          time periods (t)

  REV: array(PRODS,TIME) of real        ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real      ! Unit cost to make product p 
                                        ! at factory f
  CBUY: array(RAW,TIME) of real         ! Unit cost to buy raw materials
  REQ: array(PRODS,RAW) of real         ! Requirement by unit of product p 
                                        ! for raw material r
  MXSELL: array(PRODS,TIME) of real     ! Max. amount of p that can be sold
  MXMAKE: array(FACT) of real           ! Max. amount factory f can make 
                                        ! over all products
  IPSTOCK: array(PRODS,FACT) of real    ! Initial product stock levels 
  IRSTOCK: array(RAW,FACT) of real      ! Initial raw material stock levels
  CPSTOCK: real                         ! Unit cost to store any product p
  CRSTOCK: real                         ! Unit cost to store any raw mat. r
  MXRSTOCK: real                        ! Raw material storage capacity

  make: array(PRODS,FACT,TIME) of mpvar ! Amount of products made at factories
  sell: array(PRODS,FACT,TIME) of mpvar ! Amount of product sold from factories
  buy: array(RAW,FACT,TIME) of mpvar    ! Amount of raw material bought
  pstock: array(PRODS,FACT,1..NT+1) of mpvar ! Product stock levels at start 
                                             ! of period t
  rstock: array(RAW,FACT,1..NT+1) of mpvar   ! Raw material stock levels  
                                             ! at start of period t 
 end-declarations

 initializations from DATAFILE
  REV CMAKE CBUY REQ MXSELL MXMAKE
  IPSTOCK IRSTOCK MXRSTOCK CPSTOCK CRSTOCK
 end-initializations
 
! Product stock balance
 forall(p in PRODS,f in FACT,t in TIME)
  PBal(p,f,t):= pstock(p,f,t+1) = pstock(p,f,t) + make(p,f,t) - sell(p,f,t)

! Raw material stock balance
 forall(r in RAW,f in FACT,t in TIME) 
  RBal(r,f,t):= rstock(r,f,t+1) = 
   rstock(r,f,t) + buy(r,f,t) - sum(p in PRODS) REQ(p,r)*make(p,f,t)

! Capacity limit at factories
 forall(f in FACT,t in TIME) 
  MxMake(f,t):= sum(p in PRODS) make(p,f,t) <= MXMAKE(f)

! Limit on the amount of prod. p to be sold
 forall(p in PRODS,t in TIME) sum(f in FACT) sell(p,f,t) <= MXSELL(p,t)

! Raw material stock limit
 forall(f in FACT,t in 2..NT+1) 
  MxRStock(f,t):= sum(r in RAW) rstock(r,f,t) <= MXRSTOCK

! Initial product and raw material stock levels
 forall(p in PRODS,f in FACT) pstock(p,f,1) = IPSTOCK(p,f)
 forall(r in RAW,f in FACT) rstock(r,f,1) = IRSTOCK(r,f)

! Objective: maximize total profit
 Profit:= 
  sum(p in PRODS,f in FACT,t in TIME) REV(p,t) * sell(p,f,t) -     ! revenue
  sum(p in PRODS,f in FACT,t in TIME) CMAKE(p,f) * make(p,f,t) -   ! prod. cost
  sum(r in RAW,f in FACT,t in TIME) CBUY(r,t) * buy(r,f,t) -       ! raw mat. 
  sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * pstock(p,f,t) - ! p storage 
  sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * rstock(r,f,t)     ! r storage
   
! Solve the problem
 maximize(Profit)

! Solution printing
 writeln("Total profit: ", getobjval)
 writeln("Finished products:")
 forall(f in FACT) do
  writeln("Factory ", f, ":") 
  forall(p in PRODS) do
   write("  ", p, ": ")
   forall(t in TIME) write(strfmt(getsol(make(p,f,t)),6,1), "(", 
                           strfmt(getsol(pstock(p,f,t+1)),5,1), ")")
   writeln
  end-do 
 end-do 

 writeln("Raw material:")
 forall(f in FACT) do
  writeln("Factory ", f, ":") 
  forall(r in RAW) do
   write("  ", r, ": ")
   forall(t in TIME) write(strfmt(getsol(buy(r,f,t)),6,1), "(", 
                           strfmt(getsol(rstock(r,f,t+1)),5,1), ")")
   writeln
  end-do 
 end-do 

 writeln("Sales:")
 forall(f in FACT) do
  writeln("Factory ", f, ":") 
  forall(p in PRODS) do
   write("  ", p, ": ")
   forall(t in TIME) write(strfmt(getsol(sell(p,f,t)),4))
   writeln
  end-do 
 end-do 

end-model

cocoMs.mos
(!*******************************************************
   Mosel Example Problems 
   ======================

   file cocoMs.mos
   ```````````````
   Coco Problem, master model.
   -- Standard version --

   *** ATTENTION: This model will return an error if ***
   *** no more than one Xpress licence is available. ***

   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, 2005, rev. Jun. 2015
*******************************************************!)

model "Coco3 Master"
 uses "mmxprs","mmjobs","mmsystem"

 parameters
  DATAFILE = "coco2.dat"
  ALG = 1                             ! 0: stop phase with 1st failed subpb.
                                      ! 1: stop when all subprob.s fail
 end-parameters

 forward procedure process_sub_result
 forward procedure solve_master(phase:integer)
 forward procedure process_master_result
 forward function true_solution:real
 forward function calc_solution:real
 forward procedure print_solution
 
 declarations                         
  PHASE_0=2                           ! Event codes sent to submodels
  PHASE_1=3
  PHASE_2=4
  PHASE_3=5
  EVENT_SOLVED=6                      ! Event codes sent by submodels
  EVENT_FAILED=7
  EVENT_READY=8
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      

 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations

 declarations
  PRODS = 1..NPROD                    ! Range of products (p)
  FACT = 1..NFACT                     !          factories (f)
  RAW = 1..NRAW                       !          raw materials (r)
  TIME = 1..NT                        !          time periods (t)

  REV: array(PRODS,TIME) of real      ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real    ! Unit cost to make product p 
                                      ! at factory f
  CBUY: array(RAW,TIME) of real       ! Unit cost to buy raw materials
  MXSELL: array(PRODS,TIME) of real   ! Max. amount of p that can be sold
  CPSTOCK: real                       ! Unit cost to store any product p
  CRSTOCK: real                       ! Unit cost to store any raw mat. r

  submod: array(FACT) of Model        ! One subproblem per factory
  nIter: integer                      ! Iteration counter
  nPROP: array(FACT) of integer       ! Counters of proposals from subprob.s 
  
  Prop_make: array(PRODS,FACT,TIME,range) of real ! Amount of products made
  Prop_sell: array(PRODS,FACT,TIME,range) of real ! Amount of product sold
  Prop_buy: array(RAW,FACT,TIME,range) of real    ! Amount of raw mat. bought
  Prop_pstock: array(PRODS,FACT,1..NT+1,range) of real ! Product stock levels 
  Prop_rstock: array(RAW,FACT,1..NT+1,range) of real   ! Raw mat. stock levels  
  Prop_cost: array(FACT,range) of real  ! Cost/profit of each proposal
  Price_convex: array(FACT) of real     ! Dual price on convexity constraints
  Price_sell: array(PRODS,TIME) of real ! Dual price on sales limits
  Sol_make: array(PRODS,FACT,TIME) of real ! Solution value (products made)
  Sol_sell: array(PRODS,FACT,TIME) of real ! Solution value (product sold)
  Sol_buy: array(RAW,FACT,TIME) of real    ! Solution value (raw mat. bought)
  Sol_pstock: array(PRODS,FACT,1..NT+1) of real ! Sol. value (prod. stock) 
  Sol_rstock: array(RAW,FACT,1..NT+1) of real   ! Sol. value (raw mat. stock)
 end-declarations

 initializations from DATAFILE
  CMAKE REV CBUY MXSELL CPSTOCK CRSTOCK
 end-initializations

 initializations to "bin:shmem:pricedata"  ! Initial price data for submodels
  Price_sell 
 end-initializations

!**** Master problem ****
 declarations
  excessS: mpvar                      ! Violation of sales/buying limits
  weight: dynamic array(FACT,range) of mpvar  ! Weights for propasals

  MxSell: array(PRODS,TIME) of linctr ! Sales limit constraints
  Convex: array(FACT) of linctr       ! Convexity constraints
 end-declarations 


!**** Submodels ****
 declarations
  Stopped: set of integer
 end-declarations 

 res:= compile("g","cocoSubFs.mos")   ! Compile the submodel file
 forall(f in FACT) do                 ! Load & run one submodel per product 
  Price_convex(f):= 1
  load(submod(f), "cocoSubFs.bim")
  submod(f).uid:= f
  setworkdir(submod(f), ".")
  run(submod(f), "Factory=" + f + ",DATAFILE=" + DATAFILE)
  wait                                ! Wait for child model to be ready
  ev:=getnextevent
  if ev.class=EVENT_END then
   writeln("*** Cannot start all necessary models - aborting ***")
   exit(1)
  end-if
 end-do

!**** Phase 0: Crash ****
 nIter:=1; finished:=false
 writeln("\nPHASE 0 -- Iteration ", nIter); fflush

 forall(f in FACT)                    ! Start solving all submodels (Phase 1)
  send(submod(f), PHASE_0, 0)

 forall(f in FACT) do
  wait                                ! Wait for child (termination) events
  ev:= getnextevent
  if getclass(ev)=EVENT_SOLVED then
   process_sub_result                 ! Add new proposal to master problem
  elif getclass(ev)=EVENT_FAILED then
   finished:= true
  end-if
 end-do

 if finished then
  writeln("Problem is infeasible")
  exit(1)
 end-if

 solve_master(1)                      ! Solve the updated Ph. 1 master problem
 process_master_result                ! Store initial pricing data for submodels
!) 
 
!**** Phase 1: proposal generation (feasibility) ****
 repeat
  noimprove:= 0
  nIter+=1
  writeln("\nPHASE 1 -- Iteration ", nIter); fflush

  forall(f in FACT)                   ! Start solving all submodels (Phase 1)
   send(submod(f), PHASE_1, Price_convex(f))

  forall(f in FACT) do
   wait                               ! Wait for child (termination) events
   ev:= getnextevent
   if getclass(ev)=EVENT_SOLVED then
    process_sub_result                ! Add new proposal to master problem
   elif getclass(ev)=EVENT_FAILED then
    noimprove += 1
   end-if
  end-do

  if noimprove = NFACT then 
   writeln("Problem is infeasible")
   exit(2)
  end-if
  if ALG=0 and noimprove > 0 then 
   writeln("No improvement by some subproblem(s)")
   break
  end-if

  solve_master(1)                     ! Solve the updated Ph. 1 master problem
  if getobjval>0.00001 then
   process_master_result              ! Store new pricing data for submodels
  end-if
 until getobjval <= 0.00001
 

!**** Phase 2: proposal generation (optimization) ****
 writeln("\n**** PHASE 2 ****")
 finished:=false
 repeat
  solve_master(2)                     ! Solve Phase 2 master problem
  process_master_result               ! Store new pricing data for submodels

  nIter+=1
  writeln("\nPHASE 2 -- Iteration ", nIter); fflush

  forall(f in FACT)                   ! Start solving all submodels (Phase 2)
   send(submod(f), PHASE_2, Price_convex(f))

  forall(f in FACT) do
   wait                               ! Wait for child (termination) events
   ev:= getnextevent
   if getclass(ev)=EVENT_SOLVED then
    process_sub_result                ! Add new proposal to master problem
   elif getclass(ev)=EVENT_FAILED then        
    if ALG=0 then
     finished:=true                   ! 1st submodel w/o prop. stops phase 2
    else
     Stopped += {ev.fromuid}          ! Stop phase 2 only when no submodel
                                      ! generates a new proposal 
    end-if 
   end-if
  end-do

  if getsize(Stopped) = NFACT then finished:= true; end-if
  
 until finished


!**** Phase 3: solution to the original problem ****
 writeln("\n**** PHASE 3 ****")
 forall(f in FACT) do
  send(submod(f), PHASE_3, 0)         ! Stop all submodels
  wait
  dropnextevent
 end-do

! writeln("Total Profit=", calc_solution)
 writeln("Total Profit=", true_solution)
 print_solution
 

 fdelete("cocoSubFs.bim")             ! Cleaning up temporary files
 fdelete("shmem:pricedata")
 
!-----------------------------------------------------------
! Process the proposal generated by a subproblem
 procedure process_sub_result
  declarations
   f: integer                         ! Factory index
                                      ! Solution values of the proposal:
   sol_make: array(PRODS,TIME) of real      ! Amount of products made
   sol_sell: array(PRODS,TIME) of real      ! Amount of product sold
   sol_buy: array(RAW,TIME) of real         ! Amount of raw mat. bought
   sol_pstock: array(PRODS,1..NT+1) of real ! Product stock levels 
   sol_rstock: array(RAW,1..NT+1) of real   ! Raw mat. stock levels  
   pc: real                           ! Cost of the proposal
  end-declarations

 ! Read proposal data from memory
  initializations from "mempipe:noindex,sol"
   f
   sol_make sol_sell sol_buy sol_pstock sol_rstock
   pc
  end-initializations

 ! Add the new proposal to the master problem
  nPROP(f)+=1
  create(weight(f,nPROP(f)))
  forall(p in PRODS,t in TIME) do
   Prop_make(p,f,t,nPROP(f)):= sol_make(p,t)
   Prop_sell(p,f,t,nPROP(f)):= sol_sell(p,t)  
  end-do   
  forall(r in RAW,t in TIME) Prop_buy(r,f,t,nPROP(f)):= sol_buy(r,t)
  forall(p in PRODS,t in 1..NT+1) Prop_pstock(p,f,t,nPROP(f)):= sol_pstock(p,t)
  forall(r in RAW,t in 1..NT+1) Prop_rstock(r,f,t,nPROP(f)):= sol_rstock(r,t)
  Prop_cost(f,nPROP(f)):= pc
  writeln("Sol. for factory ", f, ":\n  make:   ", sol_make, "\n  sell:   ",
           sol_sell, "\n  buy:    ", sol_buy, "\n  pstock: ", sol_pstock, 
	   "\n  rstock: ", sol_rstock)
 end-procedure

!-----------------------------------------------------------
! (Re)solve the master problem
 procedure solve_master(phase: integer)
  forall(f in FACT)
   Convex(f):= sum (k in 1..nPROP(f)) weight(f,k) = 1

  if phase=1 then
   forall(p in PRODS,t in TIME)
    MxSell(p,t):=
     sum(f in FACT,k in 1..nPROP(f)) Prop_sell(p,f,t,k)*weight(f,k) -
      excessS <= MXSELL(p,t)
   minimize(excessS)
  else
   forall(p in PRODS,t in TIME)
    MxSell(p,t):=
     sum(f in FACT,k in 1..nPROP(f)) Prop_sell(p,f,t,k)*weight(f,k) <=
      MXSELL(p,t)
   maximize(sum(f in FACT, k in 1..nPROP(f)) Prop_cost(f,k) * weight(f,k))
  end-if

  writeln("Master problem objective: ", getobjval)
  write("  Weights:")
  forall(f in FACT,k in 1..nPROP(f)) write(" ", getsol(weight(f,k)))
  writeln
 end-procedure

!-----------------------------------------------------------
! Update pricing data for subproblems
 procedure process_master_result
  forall(p in PRODS,t in TIME) Price_sell(p,t):=getdual(MxSell(p,t))
  forall(f in FACT) Price_convex(f):=getdual(Convex(f))

  initializations to "bin:shmem:pricedata"
   Price_sell
  end-initializations
 end-procedure

!-----------------------------------------------------------
! Calculate solution to the original problem
 function true_solution: real 
  forall(p in PRODS,f in FACT,t in TIME) do
   Sol_sell(p,f,t):= 
    sum(k in 1..nPROP(f)) Prop_sell(p,f,t,k) * getsol(weight(f,k))
   Sol_make(p,f,t):= 
    sum(k in 1..nPROP(f)) Prop_make(p,f,t,k) * getsol(weight(f,k))
  end-do
  forall(r in RAW,f in FACT,t in TIME) Sol_buy(r,f,t):= 
    sum(k in 1..nPROP(f)) Prop_buy(r,f,t,k) * getsol(weight(f,k))
  forall(p in PRODS,f in FACT,t in 1..NT+1) Sol_pstock(p,f,t):=
   sum(k in 1..nPROP(f)) Prop_pstock(p,f,t,k) * getsol(weight(f,k)) 
  forall(r in RAW,f in FACT,t in 1..NT+1) Sol_rstock(r,f,t):=
   sum(k in 1..nPROP(f)) Prop_rstock(r,f,t,k) * getsol(weight(f,k)) 

  returned:=
   sum(p in PRODS,f in FACT,t in TIME) REV(p,t) * Sol_sell(p,f,t) -
   sum(p in PRODS,f in FACT,t in TIME) CMAKE(p,f) * Sol_make(p,f,t) -
   sum(r in RAW,f in FACT,t in TIME) CBUY(r,t) * Sol_buy(r,f,t) - 
   sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * Sol_pstock(p,f,t) - 
   sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * Sol_rstock(r,f,t) 
 end-function

! Solve the original problem
 function calc_solution: real 
  declarations
   make: array(PRODS,FACT,TIME) of mpvar ! Amount of products made
   sell: array(PRODS,FACT,TIME) of mpvar ! Amount of product sold
   buy: array(RAW,FACT,TIME) of mpvar    ! Amount of raw material bought
   pstock: array(PRODS,FACT,1..NT+1) of mpvar ! Product stock levels at start 
                                              ! of period t
   rstock: array(RAW,FACT,1..NT+1) of mpvar   ! Raw material stock levels  
                                              ! at start of period t 
  end-declarations
 
  forall(p in PRODS,f in FACT,t in TIME) do
   sell(p,f,t) = sum(k in 1..nPROP(f)) Prop_sell(p,f,t,k) * weight(f,k)
   make(p,f,t) = sum(k in 1..nPROP(f)) Prop_make(p,f,t,k) * weight(f,k)
  end-do
  forall(r in RAW,f in FACT,t in TIME) 
   buy(r,f,t) = sum(k in 1..nPROP(f)) Prop_buy(r,f,t,k) * weight(f,k)
  forall(p in PRODS,f in FACT,t in 1..NT+1) 
   pstock(p,f,t) = sum(k in 1..nPROP(f)) Prop_pstock(p,f,t,k) * weight(f,k)
  forall(r in RAW,f in FACT,t in 1..NT+1) 
   rstock(r,f,t) = sum(k in 1..nPROP(f)) Prop_rstock(r,f,t,k) * weight(f,k)

  MaxProfit:= 
  sum(p in PRODS,f in FACT,t in TIME) REV(p,t) * sell(p,f,t) -     ! revenue
  sum(p in PRODS,f in FACT,t in TIME) CMAKE(p,f) * make(p,f,t) -   ! prod. cost
  sum(r in RAW,f in FACT,t in TIME) CBUY(r,t) * buy(r,f,t) -       ! raw mat. 
  sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * pstock(p,f,t) - ! p storage 
  sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * rstock(r,f,t)     ! r storage 
  
  maximize(MaxProfit)
  
  returned:= getobjval

  forall(p in PRODS,f in FACT,t in TIME) do
   Sol_sell(p,f,t):= getsol(sell(p,f,t))
   Sol_make(p,f,t):= getsol(make(p,f,t))
  end-do
  forall(r in RAW,f in FACT,t in TIME) Sol_buy(r,f,t):= getsol(buy(r,f,t))
  forall(p in PRODS,f in FACT,t in 1..NT+1) 
   Sol_pstock(p,f,t):= getsol(pstock(p,f,t)) 
  forall(r in RAW,f in FACT,t in 1..NT+1) 
   Sol_rstock(r,f,t):= getsol(rstock(r,f,t)) 
 end-function
 
!----------------------------------------------------------- 
 procedure print_solution
 
  writeln("Finished products:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(p in PRODS) do
    write("  ", p, ":    ")
    forall(t in TIME) write(strfmt(Sol_make(p,f,t),6,1), "(", 
                            strfmt(Sol_pstock(p,f,t+1),5,1), ")")
    writeln
   end-do 
  end-do 

  writeln("Raw material:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(r in RAW) do
    write("  ", r, ": ")
    forall(t in TIME) write(strfmt(Sol_buy(r,f,t),6,1), "(", 
                            strfmt(Sol_rstock(r,f,t+1),5,1), ")")
    writeln
   end-do 
  end-do 

  writeln("Sales:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(p in PRODS) do
    write("  ", p, ": ")
    forall(t in TIME) write(strfmt(Sol_sell(p,f,t),4))
    writeln
   end-do 
  end-do 

  writeln("\nComputation time: ", gettime)
 end-procedure


end-model

cocoSubFs.mos
(!*******************************************************
   Mosel Example Problems 
   ======================

   file cocoSubFs.mos
   ``````````````````
   Coco Problem, single factory subproblem.
   -- Standard version --

   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, 2005, rev. Jan. 2013
*******************************************************!)

model "Coco Subproblem (factory based decomp.)"
 uses "mmxprs", "mmjobs"

 parameters
  Factory = 0
  TOL = 0.00001
  DATAFILE = "coco3.dat"
 end-parameters

 forward procedure process_solution
 
 declarations                         
  PHASE_0=2                             ! Event codes sent to submodels
  PHASE_1=3
  PHASE_2=4
  PHASE_3=5
  EVENT_SOLVED=6                        ! Event codes sent by submodels
  EVENT_FAILED=7
  EVENT_READY=8
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      

 send(EVENT_READY,0)                    ! Model is ready (= running)

 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations

 declarations
  PRODS = 1..NPROD                      ! Range of products (p)
  FACT = 1..NFACT                       !          factories (f)
  RAW = 1..NRAW                         !          raw materials (r)
  TIME = 1..NT                          !          time periods (t)

  REV: array(PRODS,TIME) of real        ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real      ! Unit cost to make product p 
                                        ! at factory f
  CBUY: array(RAW,TIME) of real         ! Unit cost to buy raw materials
  REQ: array(PRODS,RAW) of real         ! Requirement by unit of product p 
                                        ! for raw material r
  MXSELL: array(PRODS,TIME) of real     ! Max. amount of p that can be sold
  MXMAKE: array(FACT) of real           ! Max. amount factory f can make 
                                        ! over all products
  IPSTOCK: array(PRODS,FACT) of real    ! Initial product stock levels 
  IRSTOCK: array(RAW,FACT) of real      ! Initial raw material stock levels
  CPSTOCK: real                         ! Unit cost to store any product p
  CRSTOCK: real                         ! Unit cost to store any raw mat. r
  MXRSTOCK: real                        ! Raw material storage capacity
  
  make: array(PRODS,TIME) of mpvar      ! Amount of products made at factory
  sell: array(PRODS,TIME) of mpvar      ! Amount of product sold from factory
  buy: array(RAW,TIME) of mpvar         ! Amount of raw material bought
  pstock: array(PRODS,1..NT+1) of mpvar ! Product stock levels at start 
                                        ! of period t
  rstock: array(RAW,1..NT+1) of mpvar   ! Raw material stock levels  
                                        ! at start of period t 

  sol_make: array(PRODS,TIME) of real   ! Amount of products made
  sol_sell: array(PRODS,TIME) of real   ! Amount of product sold
  sol_buy: array(RAW,TIME) of real      ! Amount of raw mat. bought
  sol_pstock: array(PRODS,1..NT+1) of real ! Product stock levels 
  sol_rstock: array(RAW,1..NT+1) of real   ! Raw mat. stock levels
   
  Profit: linctr                        ! Profit of proposal
  Price_sell: array(PRODS,TIME) of real ! Dual price on sales limits
 end-declarations

 initializations from DATAFILE
  REV CMAKE CBUY REQ MXSELL MXMAKE
  IPSTOCK IRSTOCK MXRSTOCK CPSTOCK CRSTOCK
 end-initializations
 
! Product stock balance
 forall(p in PRODS,t in TIME)
  PBal(p,t):= pstock(p,t+1) = pstock(p,t) + make(p,t) - sell(p,t)

! Raw material stock balance
 forall(r in RAW,t in TIME) 
  RBal(r,t):= rstock(r,t+1) = 
   rstock(r,t) + buy(r,t) - sum(p in PRODS) REQ(p,r)*make(p,t)

! Capacity limit at factories
 forall(t in TIME) 
  MxMake(t):= sum(p in PRODS) make(p,t) <= MXMAKE(Factory)

! Limit on the amount of prod. p to be sold
 forall(p in PRODS,t in TIME) sell(p,t) <= MXSELL(p,t)
 
! Raw material stock limit
 forall(t in 2..NT+1) 
  MxRStock(t):= sum(r in RAW) rstock(r,t) <= MXRSTOCK

! Initial product and raw material stock levels
 forall(p in PRODS) pstock(p,1) = IPSTOCK(p,Factory)
 forall(r in RAW) rstock(r,1) = IRSTOCK(r,Factory)
 
! Total profit
 Profit:= 
  sum(p in PRODS,t in TIME) REV(p,t) * sell(p,t) -           ! revenue
  sum(p in PRODS,t in TIME) CMAKE(p,Factory) * make(p,t) -   ! prod. cost
  sum(r in RAW,t in TIME) CBUY(r,t) * buy(r,t) -             ! raw mat. 
  sum(p in PRODS,t in 2..NT+1) CPSTOCK * pstock(p,t) -       ! p storage 
  sum(r in RAW,t in 2..NT+1) CRSTOCK * rstock(r,t)           ! r storage   

! (Re)solve this model until it is stopped by event "PHASE_3"
 repeat
  wait
  ev:= getnextevent
  Phase:= getclass(ev)
  if Phase=PHASE_3 then               ! Stop the execution of this model
   break
  end-if
  Price_convex:= getvalue(ev)         ! Get new pricing data

  if Phase<>PHASE_0 then
   initializations from "bin:shmem:pricedata"
    Price_sell
   end-initializations
  end-if
  
 ! (Re)solve this model
  if Phase=PHASE_0 then
   maximize(Profit)
  elif Phase=PHASE_1 then
   maximize(sum(p in PRODS,t in TIME) Price_sell(p,t)*sell(p,t) + Price_convex)
  else        ! PHASE 2
   maximize(
    Profit - sum(p in PRODS,t in TIME) Price_sell(p,t)*sell(p,t) - 
     Price_convex)
  end-if

  writeln("Factory ", Factory, " - Obj: ", getobjval, 
          " Profit: ", getsol(Profit), " Price_sell: ",
          getsol(sum(p in PRODS,t in TIME) Price_sell(p,t)*sell(p,t) ), 
          " Price_convex: ", Price_convex)
  fflush
  
  if getobjval > TOL then             ! Solution found: send values to master
   process_solution
  elif getobjval <= TOL then          ! Problem is infeasible (Phase 0/1) or
   send(EVENT_FAILED,0)               ! no improved solution found (Phase 2)
  else
   send(EVENT_READY,0)
  end-if   
 until false

!-----------------------------------------------------------
! Process solution data
 procedure process_solution
  forall(p in PRODS,t in TIME) do
   sol_make(p,t):= getsol(make(p,t))
   sol_sell(p,t):= getsol(sell(p,t))  
  end-do   
  forall(r in RAW,t in TIME) sol_buy(r,t):= getsol(buy(r,t))
  forall(p in PRODS,t in 1..NT+1) sol_pstock(p,t):= getsol(pstock(p,t))
  forall(r in RAW,t in 1..NT+1) sol_rstock(r,t):= getsol(rstock(r,t))
  Prop_cost:= getsol(Profit)
  send(EVENT_SOLVED,0)

  initializations to "mempipe:noindex,sol"
   Factory
   sol_make sol_sell sol_buy sol_pstock sol_rstock
   Prop_cost
  end-initializations
 end-procedure 
end-model