Download
package psp
import oscar.cp.modeling._
import oscar.cp._
import scala.io.Source
import oscar.util._
//import stockingCost._
//import stockingCost._
/**
*
* Production planning problems, such as Lot Sizing and Scheduling Problems,
* require one to determine a minimum cost production schedule to satisfy
* the demands for single or multiple items without exceeding machine
* capacities while satisfying demands.
*
* The Lot Sizing problem considered here is a discrete, multi-item,
* single machine problem with capacity of production limited to one per period.
* There are storage costs and sequence-dependent changeover costs,
* respecting the triangle inequality.
* The changeover cost $q^{i, j}$ is induced when passing from the production
* of item $i$ to another one $j$ with $q^{i, i} = 0 for all i$.
* Backlogging is not allowed, each order has to be produced before
* the corresponding demand time, and stocking (inventory) costs has to be paid
* proportional to the number of days between the production date and
* the demand time.
* The objective is to minimize the sum of stocking costs and change over costs.
*
* @author Vinasetan Ratheil Houndji, ratheilesse@gmail.com
* @author Pierre Schaus, pschaus@gmail.com
*
*/
/*
* Instances : 15a 15b 15c 30a 30b 30c 100a 100b 100c 200a
* Available at http://becool.info.ucl.ac.be/resources/discrete-lot-sizing-problem
*/
object DiscreteLotSizing extends App {
val stockingConstraint: Int = 1
/*
* stockingConstraint = 0 => Basic decomposition
* stockingConstraint = 1 => AllDifferentBC and (sum(dealine(p) - date(p) <= H)
* stockingConstraint = 2 => minAssignment and AllDifferentBC
* stockingConstraint = 3 => stockingCost constraint
*/
//******************** Problem Data *******************************
val src = "ratheil/src/main/scala/data/pigment30a.txt"
val lines = Source.fromFile(src).getLines.reduceLeft(_ + " " + _)
val vals = lines.split("[ ,\t]").toList.filterNot(_ == "").map(_.toInt)
var index = 0
def next() = {
index += 1
vals(index - 1)
}
println(vals)
val nbPeriods = next()
val nbItems = next()
val demands = Array.fill(nbItems, nbPeriods)(next)
val stockingCost = next
val changeOverCost = Array.tabulate(nbItems, nbItems) { case (i, j) => next }
val optimalCost = next()
for (a <- 0 until nbItems) {
println(changeOverCost(a).mkString("\t"))
}
//*****************************************************************
val cp = CPSolver()
case class Order(val article: Int, val dueDate: Int, val date: CPIntVar)
val productions = (for (a <- 0 until nbItems; t <- 0 until nbPeriods; if (demands(a)(t) == 1)) yield {
Order(a, t, CPIntVar(cp, 0 to t)) // we force it (through the domain) to be produced before due date
}).toArray
val date: Array[CPIntVar] = productions.map(_.date) :+ CPIntVar(cp, nbPeriods)
val dueDate = productions.map(_.dueDate)
val article = productions.map(_.article)
val nbDemands = article.size
println("nbPeriods: " + nbPeriods)
println("nbItems: " + nbItems)
println("nbDemands: " + nbDemands)
val successors = Array.tabulate(nbDemands + 1)(i => CPIntVar(cp, 0 to nbDemands))
val costMatrix = Array.tabulate(nbDemands + 1, nbDemands + 1) {
case (i, j) =>
if (i == nbDemands || j == nbDemands) 0
else changeOverCost(article(i))(article(j))
}
val costMatrixHolding = Array.tabulate(nbDemands, nbPeriods) {
case (i, t) =>
if (t > dueDate(i)) 100000
else dueDate(i) - t
}
val t0 = System.currentTimeMillis()
//Constraints
cp.add(circuit(successors), Strong)
val transitionCosts: CPIntVar = sum(0 until nbDemands)(i => costMatrix(i)(successors(i)))
cp.add(minAssignment(successors, costMatrix, transitionCosts))
// stocking constraint
val stockingCosts: CPIntVar = sum(date.zip(dueDate).map { case (x, d) => -x + d })
if (stockingConstraint == 0) {
for (t <- 0 until nbPeriods) {
cp.add((sum(0 until nbDemands)(d => date(d) === t)) <= 1)
}
} else if (stockingConstraint == 1) {
cp.add(allDifferent(date), Medium)
cp.add(stockingCosts == -sum(0 until nbDemands)(d => date(d) - dueDate(d)))
} else if (stockingConstraint == 2) {
cp.add(allDifferent(date), Medium)
cp.add(minAssignment(date.take(nbDemands), costMatrixHolding, stockingCosts))
} else if (stockingConstraint == 3) {
cp.add(new StockingCost(date.take(nbDemands), dueDate, stockingCosts, 1))
}
// symmetry break
for (p <- 0 until (nbDemands - 1); if (article(p) == article(p + 1))) {
cp.add(date(p) < date(p + 1))
}
for (p1 <- 0 until (nbDemands - 1); p2 <- p1 until nbDemands; if (article(p1) == article(p2))) {
cp.add(successors(p2) != p1)
}
for (p <- 0 until nbDemands) {
cp.add(date(p) < date(successors(p)))
}
//objective
val obj: CPIntVar = (stockingCosts * stockingCost) + transitionCosts
cp.minimize(obj)
//search
cp.search {
binaryStatic(date) ++ binaryStatic(successors)
}
val stats = cp.start()
println(stats)
println(src)
println("stockingConstraint: " + stockingConstraint)
println("optimalCost: " + optimalCost)
}