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

Boxes - Nonlinear constraints

Description
Stating a small production planning problem with nonlinear constraints to determine the size of objects to be produced.

boxes_java.zip[download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
Boxes02.java[download]





Boxes02.java

// (c) 2023-2024 Fair Isaac Corporation

import static com.dashoptimization.objects.Utils.div;
import static com.dashoptimization.objects.Utils.mul;
import static com.dashoptimization.objects.Utils.plus;
import static com.dashoptimization.objects.Utils.pow;
import static com.dashoptimization.objects.Utils.sum;

import com.dashoptimization.ColumnType;
import com.dashoptimization.DefaultMessageListener;
import com.dashoptimization.XPRSconstants;
import com.dashoptimization.XPRSenumerations;
import com.dashoptimization.objects.Expression;
import com.dashoptimization.objects.Variable;
import com.dashoptimization.objects.XpressProblem;

/**
 * A craftsman makes small wooden boxes for sale. He has four different shapes
 * or styles of box, and can make each of them in any size (keeping all the
 * dimensions in proportion). The profit he makes on a box depends on the size.
 * He has only a limited amount of the necessary wood available and a limited
 * amount of time in the week to do the work. How many boxes should he make, and
 * what size should they be, in order to maximize his profit?
 */
public class Boxes02 {

    /** A box. */
    public static final class Box {
        /** The name of this box. */
        public final String name;
        /** Relative length of this box. */
        public final double lengthCoeff;
        /** Relative width of this box. */
        public final double widthCoeff;
        /** Relative height of this box. */
        public final double heightCoeff;
        /** Coefficient for the profit of this box, relative to its size. */
        public final double profitCoeff;
        /** Coefficient for the production time of this box, relative to its ply. */
        public final double timeCoeff;

        public Box(String name, double lengthCoeff, double widthCoeff, double heightCoeff, double profitCoeff,
                double timeCoeff) {
            this.name = name;
            this.lengthCoeff = lengthCoeff;
            this.widthCoeff = widthCoeff;
            this.heightCoeff = heightCoeff;
            this.profitCoeff = profitCoeff;
            this.timeCoeff = timeCoeff;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    /** The boxes used in this example. */
    private static final Box[] boxArray = new Box[] { new Box("Cube", 1, 1, 1, 20, 1),
            new Box("Oblong", 1, 2, 1, 27.3, 1), new Box("Flat", 4, 4, 1, 90, 1),
            new Box("Economy", 1, 2, 1, 10, 0.2) };

    /** The resource constraints used in this example. */
    static final double maxSize = 2.0;
    static final int maxNumProduced = 6;
    static final double maxBattens = 200.0;
    static final double maxPly = 210.0;
    static final double maxMakeTime = 35.0;

    public static void main(String[] args) {
        try (XpressProblem prob = new XpressProblem()) {
            // Output all messages.
            prob.callbacks.addMessageCallback(DefaultMessageListener::console);

            /**** VARIABLES ****/

            // the number of each of the boxes that should be produced
            Variable[] numProduced = prob.addVariables(boxArray.length)
                    .withName(i -> String.format("numProduced_%d", i)).withType(ColumnType.Integer)
                    .withUB(maxNumProduced).toArray();

            // the relative size (a factor of length/width/height) of each of the boxes that
            // should be produced
            Variable[] size = prob.addVariables(boxArray.length).withName(i -> String.format("size_%d", i))
                    .withUB(maxSize).toArray();

            // objective transfer column
            Variable objtransfercol = prob.addVariable(XPRSconstants.MINUSINFINITY, XPRSconstants.PLUSINFINITY,
                    ColumnType.Continuous, "objTransferCol");

            // some general characteristics of the produced boxes that will be used in the
            // model
            final Expression[] battens = new Expression[boxArray.length];
            final Expression[] ply = new Expression[boxArray.length];
            final Expression[] profit = new Expression[boxArray.length];
            final Expression[] makeTime = new Expression[boxArray.length];

            for (int i = 0; i < boxArray.length; ++i) {
                // battens = 4 * (lengthCoeff + widhtCoeff + heightCoeff) * size
                battens[i] = size[i]
                        .mul(4.0 * (boxArray[i].lengthCoeff + boxArray[i].widthCoeff + boxArray[i].heightCoeff));
                // ply = 2 * (lengthCoeff * widthCoeff + widthC * heightC + heightC * lengthC) *
                // size^2
                ply[i] = mul(pow(size[i], 2.0),
                        2.0 * (boxArray[i].lengthCoeff * boxArray[i].widthCoeff
                                + boxArray[i].widthCoeff * boxArray[i].heightCoeff
                                + boxArray[i].heightCoeff * boxArray[i].lengthCoeff));
                // profit = profitCoeff * size^1.5
                profit[i] = mul(boxArray[i].profitCoeff, pow(size[i], 1.5));
                // makeTime = 1 + timeCoeff * 1.5^(ply/10)
                makeTime[i] = plus(1.0, mul(boxArray[i].timeCoeff, pow(1.5, div(ply[i], 10.0))));
            }

            // objective function: sum(boxes) numProduced[box] * profit[box]
            Expression totalProfit = sum(boxArray.length, i -> mul(numProduced[i], profit[i]));

            // To make the objective linear, just maximize the objtransfercol
            prob.setObjective(objtransfercol, XPRSenumerations.ObjSense.MAXIMIZE);

            // Add the objective transfer row: objtransfercol = totalProfit
            prob.addConstraint(objtransfercol.eq(totalProfit));

            // limits on resource availability
            // sum of battens of all produced boxes <= maxBattens
            prob.addConstraint(sum(boxArray.length, i -> mul(numProduced[i], battens[i])).leq(maxBattens));
            // sum of ply of all produced boxes <= maxPly
            prob.addConstraint(sum(boxArray.length, i -> mul(numProduced[i], ply[i])).leq(maxPly));
            // sum of make time of all produced boxes <= maxMakeTime
            prob.addConstraint(sum(boxArray.length, i -> mul(numProduced[i], makeTime[i])).leq(maxMakeTime));

            // Dump the problem to disk so that we can inspect it.
            prob.writeProb("boxes02.lp", "l");

            // By default we will solve to global optimality, uncomment for an MISLP solve
            // to local optimality
            // prob.controls().setNLPSolver(XPRSconstants.NLPSOLVER_LOCAL);

            // Solve
            prob.optimize();
            if (prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.OPTIMAL
                    && prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.FEASIBLE)
                throw new RuntimeException("optimization failed with status " + prob.attributes().getSolStatus());
            double[] sol = prob.getSolution();
            // Printing the objective is currently not supported for nonlinear
            // System.out.println("Objective: " + prob.attributes().getNLPObjVal());
            // Print out the solution
            for (int i = 0; i < boxArray.length; ++i) {
                if (numProduced[i].getValue(sol) > 0.0) {
                    System.out.println("Producing " + numProduced[i].getValue(sol) + " " + boxArray[i].toString()
                            + " boxes of size " + size[i].getValue(sol) + ".");
                }
            }
        }
    }
}

Back to examples browserPrevious exampleNext example