Initializing help system before first use

Boxes - Nonlinear constraints


Type: Production Planning
Rating: 2 (easy-medium)
Description: Stating a small production planning problem with nonlinear constraints to determine the size of objects to be produced.
File(s): Boxes02.java


Boxes02.java
// (c) 2023-2025 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) + ".");
                }
            }
        }
    }
}

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.