Lindo Systems

! Model of a 3 level, PLant -> DC -> customer supply chain;
! Keywords: @IN, @INSERT, Set generation, Supply chain;
sets:
 plant: pcap;
 dc;
 cust: cdem;
 pxd( plant, dc): pdcost, pdship;
 dxc( dc, cust): dccost, dcship;
! Supplemental sets;
 pxdxc( plant, dc, cust): pdcvol;
 pxc( plant, cust): pcvol;
endsets
data:
! The plants and their capacities;
  plant, pcap =
   A      9
   B      8 
; 
! The Distribution Centers;
  dc = X, Y, Z   
;
! Customers and their demands;
  cust, cdem =
   C1    3
   C2    5
   C3    4
   C4    2
;
! Cost/unit of shipping from a plant to a DC;
  pxd,  pdcost=
  A  X    1
  A  Y    2
  B  Y    1
  B  Z    2
;
! Cost/unit of shipping from a DC to a customer;
  dxc,  dccost=
  X C1   5
  X C2   8
  Y C1   9
  Y C2   6
  Y C3   7
  Z C2   8
  Z C3   7
  Z C4   4
;
enddata
submodel supplychain:
! Minimize the cost of plant to DC shipments plus
       cost of DC to customer shipments;
  min = costpd + costdc;
  costpd = @sum( pxd( p, d): pdcost( p, d) * pdship( p, d));
  costdc = @sum( dxc( d, c): dccost( d, c) * dcship( d, c));

! Plant capacity constraints;
  @for( plant( p):
     pcap( p) >= @sum( pxd( p, d): pdship( p, d));
      );

! Flow balance at each DC;
  @for( dc( d):
    @sum( pxd( p, d): pdship( p, d)) = @sum( dxc( d, c): dcship( d, c));
      );

! Demand constraint at each customer;
  @for( cust( c):
    @sum( dxc( d, c): dcship( d, c)) = cdem( c);
      );

! Add constraints to force consistency between.. ;
!   the pdcvol variables.. ;
!     and the pdship variables;
  @for( pxd( p, d):
    pdship( p, d) = @sum( pxdxc( p, d, c): pdcvol( p, d, c));
      );
!     and the dcship variables;
  @for( dxc( d, c):
     dcship( d, c) = @sum( pxdxc( p, d,c): pdcvol( p, d, c));
      );
!    and the pcvol variables;
   @for( pxc( p, c):
      pcvol( p, c) = @sum( pxdxc( p, d, c): pdcvol( p, d, c));
       );

endsubmodel

calc:
! Construct the supplemental sets;
! Element ( p, d, c) is in pxdxc only if the path p->d->c exists;
 @for( pxd( p, d):      ! Can ship from p to d;
   @for( dxc( d, c):    ! Can ship from d to c;
     @insert( pxdxc, p, d, c);  ! Put it in the set;
       );
    );

 ! Element ( p, c) is in pxc only if there is some path from p to c;
 @for( plant( p):
   @for( cust( c):
 ! If there is some DC d such that p->d exists and d->c exists,
   then add ( p, c) to set pxc;
     @ifc( @sum( pxd( p, d): @in( pxd, p, d) #and# @in( dxc, d, c)) #ge# 1:
        @insert( pxc, p, c);
         );
       );
     );
  @solve( supplychain);

  @write( '   Solution report', @newline(1));
  @write( ' Total cost= ', costpd + costdc, @newline( 1));
  @write( ' Inbound costs= ', costpd,',  Outbound costs= ', costdc, @newline(1));
  @write( '      Plant      DC    Shipped', @newline(1));
  @for( pxd( p, d) | pdship( p, d) #GT# 0:
      @write( @format( plant( p),'9s'),' ',@format( dc( d),'9s'), 
              @format( pdship( p, d),'9.2f'), @newline(1));
      ); 

  @write( @newline(1),'       DC       Cust   Shipped', @newline(1));
  @for( dxc( d, c) | dcship( d, c) #GT# 0:
      @write( @format( dc( d),'9s'),' ',@format( cust( c),'9s'), 
              @format( dcship( d, c),'9.2f'), @newline(1));
      );

 @write( @newline(1),'      Plant     Cust    Volume', @newline( 1));
  @for( pxc( p, c):
         @write( @format( plant( p),'9s'),' ',@format( cust( c),'9s'), 
              ' ', @format( pcvol( p, c), '9.2f'), @newline(1));
      );

 @write( @newline(1),'      Plant      DC       Cust    Volume', @newline( 1));
  @for( pxdxc( p, d, c):
         @write( @format( plant( p),'9s'),' ', @format( dc( d), '9s'),' ', @format( cust( c),'9s'),
              ' ', @format( pdcvol( p, d, c), '9.2f'),  @newline(1));
      );
endcalc