FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious example

Nonlinear trimloss problem

Nonlinear formulation of trim loss problem solved either via the Xpress Nonlinear solvers (trimminlp.mos) or configurable for using alternative NLP solvers (trimminnlp2.mos).

Further explanation of this example: This model is discussed in Section 13.3 of the book 'J. Kallrath: Business Optimization Using Mathematical Programming - An Introduction with Case Studies and Solutions in Various Algebraic Modeling Languages' (2nd edition, Springer, Cham, 2021, DOI 10.1007/978-3-030-73237-0).

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


   Mosel Example Problems

   file trimminlp2.mos
   Trim loss problem 
   - Implementation with solver choice -
     Example described in section 13.3 of
     J. Kallrath: Business Optimization Using Mathematical Programming -
     An Introduction with Case Studies and Solutions in Various Algebraic 
     Modeling Languages. 2nd edition, Springer Nature, Cham, 2021 

   author: S. Heipcke, October 2020

   (c) Copyright 2020 Fair Isaac Corporation
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.


model 'trimminlpalt'
  uses "nlsolv", "mmsystem"
  ! Solver configuration: select any (previously installed) MINLP solver
  ! that supports NL format
!    SOLVER="knitroampl"
    SOLVEROPTIONS=""     ! Ex: Stop at 0.1% integrality gap, time limit

!******** Input data ********
    RPATT=1..6                    ! Set of cutting patterns
    RWDTH=1..6                    ! Set of order width types
    WDTH: array(RWDTH) of real    ! Widths of orders
    DEM: array(RWDTH) of real     ! Requirements
    CP: array(RPATT) of real      ! Pattern cost
    CR: array(RPATT) of real      ! Roll cost
    WMR: integer                  ! Width of master roll
    FWIDTH: integer               ! Min fill width
    KNIFES: integer               ! Max number of knifes available
    MAXW: array(RWDTH) of real    ! Bound on the number of width in a pattern

  WMR:= 2360; FWIDTH:= 2225; KNIFES:=12
  forall(p in RPATT) CP(p):=1
  forall(p in RPATT) CR(p):=2

!******** Problem formulation ********
    alpha: array(RWDTH,RPATT) of mpvar  ! Number of width i per pattern p
    muse: array(RPATT) of mpvar         ! How oftern pattern is used
    delta: array(RPATT) of mpvar        ! Indicate whether pattern is active
    Numpat: linctr                      ! Objective: number of patterns used

 ! Upper bound on the number of width in a pattern
  forall(i in RWDTH) MAXW(i):= minlist(floor(WMR/WDTH(i)),DEM(i))
  forall(i in RWDTH,p in RPATT) do
    alpha(i,p) is_integer
    alpha(i,p) <= MAXW(i)

 ! Upper bound on pattern multiplicity
  MAXDEM:= max(i in RWDTH) DEM(i)
  forall(p in RPATT) do
    muse(p) is_integer
    muse(p) <= MAXDEM 
  forall(p in RPATT) delta(p) is_binary

 ! Objective function: number of patterns + number of rolls used
  Numpat:= sum(p in RPATT) (CP(p)*delta(p) + CR(p)*muse(p))

 ! Fulfill demand exactly (nonlinear, nonconvex constraint)
  forall(i in RWDTH) Demand(i):= sum(p in RPATT) alpha(i,p)*muse(p) = DEM(i) 

 ! Fit into the width of the master roll
  forall(p in RPATT) MRwidth(p):= sum(i in RWDTH) WDTH(i)*alpha(i,p) <= WMR 

 ! Observe the minimal width to be filled
  forall(p in RPATT)
    MinFill(p):= sum(i in RWDTH) WDTH(i)*alpha(i,p) >= FWIDTH*delta(p)

 ! Limit the maximal available number of knifes
  forall(p in RPATT) Knifes(p):= sum(i in RWDTH) alpha(i,p) <= KNIFES

 ! Whether pattern p is used, that is, delta(p) = 1, else 0
  forall(p in RPATT) PatternUse(p):= muse(p) <= MAXDEM*delta(p)

 ! Symmetry breaking: activate pattern p only when pattern p is active
  forall(p in RPATT | p>RPATT.first) Symmetry1(p):= delta(p) <= delta(p-1) 

 ! Symmetry breaking: order the patterns with descending multiplicity
  forall(p in RPATT | p>RPATT.first) Symmetry2(p):= muse(p) <= muse(p-1)

 ! Linking alpha and delta variables
  forall(i in RWDTH,p in RPATT) Cut1(i,p):= alpha(i,p) <= MAXW(i)*delta(p) 

 ! Pattern needs to have a positive multiplicity to be used at all
  forall(p in RPATT) Cut2(p):= delta(p) <= muse(p)

!******** Minimize the number of patterns ********

! Configuration of the solver
  setparam("nl_verbose", true) 
  setparam("nl_solver", SOLVER) 
  if SOLVERPATH<>"" then
    setparam("nl_solverpath", SOLVERPATH)
  if SOLVEROPTIONS<>"" then
    setparam("nl_options", SOLVEROPTIONS)
 ! Solve the problem
  if getprobstat<>NL_OPT and getprobstat<>NL_UNF then
    writeln("No solution available. Solver status: ", getparam("nl_status"))
    writeln("Solution: ", getobjval, 
      ". Number of patterns=", getsol(sum(p in RPATT) delta(p)), 
      ". Number of rolls=", sum(p in RPATT) muse(p).sol ) 
    write("          Width ")   
    forall(i in RWDTH) write(strfmt(WDTH(i),4))
    writeln(" | Filled width")
    write("       (Demand)")  
    forall(i in RWDTH) write(strfmt(DEM(i),4))
    writeln("\n", "-"*60)
    forall(p in RPATT | muse(p).sol>0) do
      write("Pattern ", p, " x ", strfmt(muse(p).sol,2), ":")
      forall(i in RWDTH) write(strfmt(alpha(i,p).sol,4))
      writeln("  |   ",sum(i in RWDTH) WDTH(i)*alpha(i,p).sol)

Back to examples browserPrevious example