jsdp.app.lotsizing.sS_jsdp.java Source code

Java tutorial

Introduction

Here is the source code for jsdp.app.lotsizing.sS_jsdp.java

Source

/**
 * jsdp: A Java Stochastic Dynamic Programming Library
 * 
 * MIT License
 * 
 * Copyright (c) 2016 Roberto Rossi
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package jsdp.app.lotsizing;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.commons.lang3.time.StopWatch;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.stream.IntStream;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import umontreal.ssj.charts.XYLineChart;
import umontreal.ssj.probdist.Distribution;
import umontreal.ssj.probdist.PoissonDist;
import umontreal.ssj.probdist.NormalDist;
import jsdp.app.lotsizing.simulation.SimulatePolicies;

/**
 *  We formulate the stochastic lot sizing problem as defined in  
 *  
 *  Herbert E. Scarf. Optimality of (s, S) policies in the dynamic inventory problem. In K. J. Arrow, S. Karlin, 
 *  and P. Suppes, editors, Mathematical Methods in the Social Sciences, pages 196202. Stanford University
 *  Press, Stanford, CA, 1960.
 *  
 *  as a stochastic dynamic programming problem. 
 *  
 *  We use forward and backward recursion to find optimal policies.
 *  
 *  We plot the optimal expected total cost function as a function of a period opening inventory level.
 *  
 *  This example demonstrates how to develop an SDP model from scratch by defining concrete implementations
 *  of the abstract classes in package jsdp.sdp. This is a cumbersome solution that should be adopted for
 *  complex problems. In most cases, it is sufficient to rely upon general purpose concrete implementations 
 *  in package jsdp.sdp.impl.  
 *  
 * @author Roberto Rossi
 *
 */
public class sS_jsdp {

    static final Logger logger = LogManager.getLogger(sS_jsdp.class.getName());

    public static void main(String[] args) {
        //Factor must be 1 for discrete distributions
        sS_State.setStateBoundaries(1, -100, 100);

        double fixedOrderingCost = 50;
        double proportionalOrderingCost = 0;
        double holdingCost = 1;
        double penaltyCost = 4;

        double[] demand = { 20, 30, 20, 40 };

        /*Distribution[] distributions = IntStream.iterate(0, i -> i + 1)
                                          .limit(demand.length)
                                          .mapToObj(i -> new NormalDist(demand[i],demand[i]*0.4))
                                          .toArray(Distribution[]::new);*/

        Distribution[] distributions = IntStream.iterate(0, i -> i + 1).limit(demand.length)
                .mapToObj(i -> new PoissonDist(demand[i])).toArray(Distribution[]::new);

        double minDemand = 0;
        double maxDemand = sS_State.getMaxInventory() - sS_State.getMinInventory();

        double initialInventory = 0;
        sS_StateSpaceSampleIterator.SamplingScheme samplingScheme = sS_StateSpaceSampleIterator.SamplingScheme.NONE;
        int maxSampleSize = 25;

        System.out.println("--------------Forward recursion--------------");
        simpleTestForward(fixedOrderingCost, proportionalOrderingCost, holdingCost, penaltyCost, distributions,
                minDemand, maxDemand, initialInventory);
        System.out.println("--------------Backward recursion--------------");
        simpleTestBackward(fixedOrderingCost, proportionalOrderingCost, holdingCost, penaltyCost, distributions,
                minDemand, maxDemand, initialInventory, samplingScheme, maxSampleSize);
        System.out.println("--------------Cost function plot--------------");
        int targetPeriod = 0;
        boolean printCostFunctionValues = false;
        boolean latexOutput = false;
        plotCostFunction(targetPeriod, fixedOrderingCost, proportionalOrderingCost, holdingCost, penaltyCost,
                distributions, minDemand, maxDemand, printCostFunctionValues, latexOutput, samplingScheme,
                maxSampleSize);
    }

    public static void simpleTestBackward(double fixedOrderingCost, double proportionalOrderingCost,
            double holdingCost, double penaltyCost, Distribution[] distributions, double minDemand,
            double maxDemand, double initialInventory, sS_StateSpaceSampleIterator.SamplingScheme samplingScheme,
            int maxSampleSize) {

        /**
         * Simulation confidence level and error threshold
         */
        double confidence = 0.95;
        double errorTolerance = 0.001;

        solveSampleInstanceBackwardRecursion(distributions, minDemand, maxDemand, fixedOrderingCost,
                proportionalOrderingCost, holdingCost, penaltyCost, initialInventory, confidence, errorTolerance,
                samplingScheme, maxSampleSize);
    }

    public static void simpleTestForward(double fixedOrderingCost, double proportionalOrderingCost,
            double holdingCost, double penaltyCost, Distribution[] distributions, double minDemand,
            double maxDemand, double initialInventory) {

        /**
         * Simulation confidence level and error threshold
         */
        double confidence = 0.95;
        double errorTolerance = 0.001;

        solveSampleInstanceForwardRecursion(distributions, minDemand, maxDemand, fixedOrderingCost,
                proportionalOrderingCost, holdingCost, penaltyCost, initialInventory, confidence, errorTolerance);
    }

    public static void solveSampleInstanceBackwardRecursion(Distribution[] distributions, double minDemand,
            double maxDemand, double fixedOrderingCost, double proportionalOrderingCost, double holdingCost,
            double penaltyCost, double initialInventory, double confidence, double errorTolerance,
            sS_StateSpaceSampleIterator.SamplingScheme samplingScheme, int maxSampleSize) {

        sS_BackwardRecursion recursion = new sS_BackwardRecursion(distributions, minDemand, maxDemand,
                fixedOrderingCost, proportionalOrderingCost, holdingCost, penaltyCost, samplingScheme,
                maxSampleSize);
        /*sS_SequentialBackwardRecursion recursion = new sS_SequentialBackwardRecursion(distributions,
                                                                                fixedOrderingCost,
                                                                                proportionalOrderingCost,
                                                                                holdingCost,
                                                                                penaltyCost);*/

        StopWatch timer = new StopWatch();
        timer.start();
        recursion.runBackwardRecursion();
        timer.stop();
        System.out.println();
        double ETC = recursion.getExpectedCost(initialInventory);
        System.out.println(
                "Expected total cost (assuming an initial inventory level " + initialInventory + "): " + ETC);
        System.out.println("Time elapsed: " + timer);
        System.out.println();

        double[][] optimalPolicy = recursion.getOptimalPolicy(initialInventory);
        double[] s = optimalPolicy[0];
        double[] S = optimalPolicy[1];
        for (int i = 0; i < distributions.length; i++) {
            System.out.println("S[" + (i + 1) + "]:" + S[i] + "\ts[" + (i + 1) + "]:" + s[i]);
        }

        DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.ENGLISH);
        DecimalFormat df = new DecimalFormat("#.00", otherSymbols);

        double[] results = SimulatePolicies.simulate_sS(distributions, fixedOrderingCost, holdingCost, penaltyCost,
                proportionalOrderingCost, initialInventory, S, s, confidence, errorTolerance);
        System.out.println();
        System.out.println("Simulated cost: " + df.format(results[0]) + " Confidence interval=("
                + df.format(results[0] - results[1]) + "," + df.format(results[0] + results[1]) + ")@"
                + df.format(confidence * 100) + "% confidence");
        System.out.println();
    }

    public static void solveSampleInstanceForwardRecursion(Distribution[] distributions, double minDemand,
            double maxDemand, double fixedOrderingCost, double proportionalOrderingCost, double holdingCost,
            double penaltyCost, double initialInventory, double confidence, double errorTolerance) {

        sS_ForwardRecursion recursion = new sS_ForwardRecursion(distributions, minDemand, maxDemand,
                fixedOrderingCost, proportionalOrderingCost, holdingCost, penaltyCost);

        sS_StateDescriptor initialState = new sS_StateDescriptor(0, sS_State.inventoryToState(initialInventory));

        /**
         * Replace parallelStream with stream in "runForwardRecursion" to prevent deadlock
         */
        StopWatch timer = new StopWatch();
        timer.start();
        recursion.runForwardRecursion(
                ((sS_StateSpace) recursion.getStateSpace()[initialState.getPeriod()]).getState(initialState));
        timer.stop();
        double ETC = recursion.getExpectedCost(initialInventory);
        double action = sS_State.stateToInventory(recursion.getOptimalAction(initialState).getIntAction());
        System.out.println(
                "Expected total cost (assuming an initial inventory level " + initialInventory + "): " + ETC);
        System.out.println("Optimal initial action: " + action);
        System.out.println("Time elapsed: " + timer);
        System.out.println();

        double[][] optimalPolicy = recursion.getOptimalPolicy(initialInventory);
        double[] s = optimalPolicy[0];
        double[] S = optimalPolicy[1];
        for (int i = 0; i < distributions.length; i++) {
            System.out.println("S[" + (i + 1) + "]:" + S[i] + "\ts[" + (i + 1) + "]:" + s[i]);
        }
        System.out.println("Note that (s,S) values for period 0 have been set on best effort basis.");

        DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.ENGLISH);
        DecimalFormat df = new DecimalFormat("#.00", otherSymbols);

        double[] results = SimulatePolicies.simulate_sS(distributions, fixedOrderingCost, holdingCost, penaltyCost,
                proportionalOrderingCost, initialInventory, recursion, confidence, errorTolerance);
        System.out.println("Simulated cost: " + df.format(results[0]) + " Confidence interval=("
                + df.format(results[0] - results[1]) + "," + df.format(results[0] + results[1]) + ")@"
                + df.format(confidence * 100) + "% confidence");
        System.out.println();
    }

    public static void plotCostFunction(int targetPeriod, double fixedOrderingCost, double proportionalOrderingCost,
            double holdingCost, double penaltyCost, Distribution[] distributions, double minDemand,
            double maxDemand, boolean printCostFunctionValues, boolean latexOutput,
            sS_StateSpaceSampleIterator.SamplingScheme samplingScheme, int maxSampleSize) {

        sS_BackwardRecursion recursion = new sS_BackwardRecursion(distributions, minDemand, maxDemand,
                fixedOrderingCost, proportionalOrderingCost, holdingCost, penaltyCost, samplingScheme,
                maxSampleSize);
        recursion.runBackwardRecursion(targetPeriod);
        XYSeries series = new XYSeries("(s,S) policy");
        for (double i = sS_State.getMinInventory(); i <= sS_State.getMaxInventory(); i += sS_State.getStepSize()) {
            sS_StateDescriptor stateDescriptor = new sS_StateDescriptor(targetPeriod, sS_State.inventoryToState(i));
            series.add(i, recursion.getExpectedCost(stateDescriptor));
            if (printCostFunctionValues)
                System.out.println(i + "\t" + recursion.getExpectedCost(stateDescriptor));
        }
        XYDataset xyDataset = new XYSeriesCollection(series);
        JFreeChart chart = ChartFactory.createXYLineChart(
                "(s,S) policy - period " + targetPeriod + " expected total cost", "Opening inventory level",
                "Expected total cost", xyDataset, PlotOrientation.VERTICAL, false, true, false);
        ChartFrame frame = new ChartFrame("(s,S) policy", chart);
        frame.setVisible(true);
        frame.setSize(500, 400);

        if (latexOutput) {
            try {
                XYLineChart lc = new XYLineChart("(s,S) policy", "Opening inventory level", "Expected total cost",
                        new XYSeriesCollection(series));
                File latexFolder = new File("./latex");
                if (!latexFolder.exists()) {
                    latexFolder.mkdir();
                }
                Writer file = new FileWriter("./latex/graph.tex");
                file.write(lc.toLatex(8, 5));
                file.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}