8.6. JAVA API

This section provides MindOpt JAVA API reference. Contents of the JAVA API are the following:

8.6.17. Get mindoptj

There are 2 ways to get mindoptj jar:

  1. Append mindoptj.jar from MindOpt installation to your classpath.

  2. With Apache Maven, add a dependency to mindoptj.

8.6.17.1. Include jar from installation

Jar file mindoptj.jar can be found in MindOpt lib directory ${INSTALL_PREFIX}/mindopt/${VERSION}/${PLATFORM_ARCH}/lib, for example /home/foo/mindopt/0.0.1/linux64-x86/lib.

Append this jar to your classpath. In addition, this jar loads necessary native libraries from MindOpt installation too, make sure java.library.path contains lib directory, and environment variable MINDOPT_HOME is correctly configured, for example export MINDOPT_HOME=/home/foo/mindopt/0.0.1.

8.6.17.2. As a maven dependency

This section is for mindoptj version >= 2.1.0 only. Previous versions are not recommended due to performance issues.

MindOpt provides different jars for different platforms. That includes:

  • “Thin” jar, this is a pure jar without native library, user is responsible to provides native libraries via environment variable (see above section).

    <dependency>
        <groupId>com.alibaba.damo</groupId>
        <artifactId>mindoptj</artifactId>
        <version>x.y.z</version>
    </dependency>
    
  • Platform-dependent jar, it contains all native libraries it may load. 5 platforms are now supported:

    • win-x64

    • linux-x64

    • linux-aarch

    • osx-x64

    • osx-aarch

    Platform is specified by providing a maven artifact classifier, for example:

    <dependency>
        <groupId>com.alibaba.damo</groupId>
        <artifactId>mindoptj</artifactId>
        <version>x.y.z</version>
        <classifier>win-x64</classifier>
    </dependency>
    
  • A really “FAT” jar with all native libraries for all platforms.

    <dependency>
        <groupId>com.alibaba.damo</groupId>
        <artifactId>mindoptj</artifactId>
        <version>x.y.z</version>
        <classifier>portable</classifier>
    </dependency>
    

As a best practice, the follwing configuration loads corresponding jar for current platform:

<project>
    ...

    <properties>
        ...
        <!-- replace 'some_version' with your expected version -->
        <mindoptj.version>some_version</mindoptj.version>
        ...
    </properties>

    <!-- test current platform & architecture to determine property 'mindoptj.platform' -->
    <profiles>
        <profile>
            <id>win-x64</id>
            <activation>
                <os>
                    <family>windows</family>
                    <arch>x86_64</arch>
                </os>
            </activation>
            <properties>
                <mindoptj.platform>win-x64</mindoptj.platform>
            </properties>
        </profile>

        <profile>
            <id>linux-x64</id>
            <activation>
                <os>
                    <name>linux</name>
                    <family>unix</family>
                    <arch>x86_64</arch>
                </os>
            </activation>
            <properties>
                <mindoptj.platform>linux-x64</mindoptj.platform>
            </properties>
        </profile>

        <profile>
            <id>linux-aarch</id>
            <activation>
                <os>
                    <name>linux</name>
                    <family>unix</family>
                    <arch>aarch64</arch>
                </os>
            </activation>
            <properties>
                <mindoptj.platform>linux-aarch64</mindoptj.platform>
            </properties>
        </profile>

        <profile>
            <id>osx-x64</id>
            <activation>
                <os>
                    <family>mac</family>
                    <arch>x86_64</arch>
                </os>
            </activation>
            <properties>
                <mindoptj.platform>osx-x64</mindoptj.platform>
            </properties>
        </profile>

        <profile>
            <id>osx-aarch</id>
            <activation>
                <os>
                    <family>mac</family>
                    <arch>aarch64</arch>
                </os>
            </activation>
            <properties>
                <mindoptj.platform>osx-aarch</mindoptj.platform>
            </properties>
            </dependencies>
        </profile>
    </profiles>

    <dependencies>
        ...
        <!-- add mindoptj with specified platform -->
        <dependency>
            <groupId>com.alibaba.damo</groupId>
            <artifactId>mindoptj</artifactId>
            <version>${mindoptj.version}</version>
            <classifier>${mindoptj.platform}</classifier>
        </dependency>
        ...
    <dependencies>

    ...
</project>

Note

If you use mindoptj which includes libraries (that is, configure mindoptj with classifier), environment variable MINDOPT_HOME will be set automatically, user defined value will be overwritten.

Additionally, if a license file (mindopt.lic, fl_client.ini, or ce_license.ini) exists in classpath, environment variable MINDOPT_LICENSE_PATH will be set properly, user defined value will be overwritten.

Warning

Platform is detected at maven compile time rather than java runtime. For example, if a fat jar file is packaged on Windows but runs on Linux Docker, it will fail.

Platform can be manually selected by mvn -P ${PROFILE_ID} if you know what platform the fat jar will run on. Otherwise the fat portable jar may be prefered.

8.6.18. Examples

Diet problem

There are many different kinds of food, each of which can provide various nutrients. How should we make decisions regarding food intake to minimize the amount spent on purchasing food while ensuring that the daily nutritional requirements of the human body are met?

Sets

  • Food set \(F\)

  • Nutrient set \(N\)

Parameters

  • The content of nutrient \(i \in N\) in one unit of food \(j\) is \(a_{ij}\) .

  • The cost of obtaining one unit of food \(j \in F\) is \(c_j\) .

Decision Variables

  • \(x_j\) is the amount of food \(j \in F\) that a person consumes daily.

Objective Function

  • Minimize the total procurement cost of food: \(\text{minimize}~ \sum_{j \in F} c_{j} x_j\) .

Constraints

  • The nutritional requirement for each nutrient \(i \in N\) must lie between the minimum value \(n_{\min,i}\) and the maximum value \(n_{\max,i}\) : \(n_{\min,i} \le \sum_{j \in F} a_{ij} x_j \le n_{\max,i}, ~~\forall i \in N\) .


import java.util.HashMap;
import java.util.Map;

public class ExampleDiet {
    public static void main(String[] args) throws MDOException{
        // Create a model
        MDOEnv env = new MDOEnv();
        MDOModel model = new MDOModel(env);
        model.set(MDO.StringAttr.ModelName, "diet");

        // Define requirements
        Map<String, double[]> requirements = new HashMap<>();
        requirements.put("Calories",    new double[]{ 2000, MDO.INFINITY });
        requirements.put("Carbohydrates",         new double[]{ 350, 375 });
        requirements.put("Protein",       new double[]{ 55, MDO.INFINITY });
        requirements.put("VitA",         new double[]{ 100, MDO.INFINITY });
        requirements.put("VitC",         new double[]{ 100, MDO.INFINITY });
        requirements.put("Calcium",      new double[]{ 100, MDO.INFINITY });
        requirements.put("Iron",         new double[]{ 100, MDO.INFINITY });
        requirements.put("Volume",        new double[]{ -MDO.INFINITY,75 });

        Map<String, double[]> foods = new HashMap<>();

        // Define foods
        foods.put("Cheeseburger",    new double[]{ 0, MDO.INFINITY, 1.84 });
        foods.put("HamSandwich",     new double[]{ 0, MDO.INFINITY, 2.19 });
        foods.put("Hamburger",       new double[]{ 0, MDO.INFINITY, 1.84 });
        foods.put("FishSandwich",    new double[]{ 0, MDO.INFINITY, 1.44 });
        foods.put("ChickenSandwich", new double[]{ 0, MDO.INFINITY, 2.29 });
        foods.put("Fries",           new double[]{ 0, MDO.INFINITY, 0.77 });
        foods.put("SausageBiscuit",  new double[]{ 0, MDO.INFINITY, 1.29 });
        foods.put("LowfatMilk",      new double[]{ 0, MDO.INFINITY, 0.60 });
        foods.put("OrangeJuice",     new double[]{ 0, MDO.INFINITY, 0.72 });

        // Define requirements and food nutrient values
        String[] nutritionNames = new String[]{ "Calories", "Carbohydrates", "Protein",
                "VitA", "VitC", "Calcium", "Iron", "Volume" };
        int numNutrition = nutritionNames.length;
        String[] foodNames = new String[]{  "Cheeseburger", "HamSandwich", "Hamburger", "FishSandwich",
                "ChickenSandwich", "Fries", "SausageBiscuit", "LowfatMilk", "OrangeJuice"  };
        int numFood = foodNames.length;
        Map<String, Map<String, Double>> reqValues = new HashMap<>();
        double[][] values = new double[][]{
                { 510.0, 34.0, 28.0, 15.0,   6.0, 30.0, 20.0,  4.0 },
                { 370.0, 35.0, 24.0, 15.0,  10.0, 20.0, 20.0,  7.5 },
                { 500.0, 42.0, 25.0,  6.0,   2.0, 25.0, 20.0,  3.5 },
                { 370.0, 38.0, 14.0,  2.0,   0.0, 15.0, 10.0,  5.0 },
                { 400.0, 42.0, 31.0,  8.0,  15.0, 15.0,  8.0,  7.3 },
                { 220.0, 26.0,  3.0,  0.0,  15.0,  0.0,  2.0,  2.6 },
                { 345.0, 27.0, 15.0,  4.0,   0.0, 20.0, 15.0,  4.1 },
                { 110.0, 12.0,  9.0, 10.0, 120.0, 30.0,  0.0,  8.0 },
                {  80.0, 20.0,  1.0,  2.0,   4.0,  2.0,  2.0, 12.0 } };
        for (int i = 0; i < foodNames.length; i++) {
            reqValues.put(foodNames[i], new HashMap<>());
            for (int j = 0; j < nutritionNames.length; j++) {
                reqValues.get(foodNames[i]).put(nutritionNames[j], values[i][j]);
            }
        }

        try {
            // Add variables
            MDOVar[] foodVars = new MDOVar[numFood];
            for (int i = 0; i < numFood; ++i) {
                double[] foodData = foods.get(foodNames[i]);
                foodVars[i] = model.addVar(foodData[0], foodData[1], 0, MDO.CONTINUOUS, foodNames[i]);
             }

            // Add constraints
            // Ensure that the intake of each nutrient is above the given lower bound and below the given upper bound
            for (int i = 0; i < numNutrition; i++) {
                MDOLinExpr linExpr = new MDOLinExpr();
                String nutri = nutritionNames[i];
                for (int j = 0; j < numFood; j++) {
                    String food = foodNames[j];
                    linExpr.addTerm(reqValues.get(food).get(nutri), foodVars[j]);
                 }
                model.addRange(linExpr, requirements.get(nutritionNames[i])[0], requirements.get(nutritionNames[i])[1], nutritionNames[i]);
             }

            // Add objective function
            MDOLinExpr linExpr = new MDOLinExpr();
            for (int i = 0; i < numFood; i++) {
                linExpr.addTerm(foods.get(foodNames[i])[2], foodVars[i]);
             }
            model.setObjective(linExpr, MDO.MINIMIZE);

            // Optimize the model
            model.optimize();

            model.write("TestJava.mps");

            // Print the results
            for (MDOVar foodVar : foodVars) {
                System.out.println("You should buy " + foodVar.get(MDO.DoubleAttr.X) + " unit of " + foodVar.get(MDO.StringAttr.VarName));
             }
         } catch (MDOException e) {
            System.out.println(e.getMessage());
         } finally {
            // Dispose of model and environment
            model.dispose();
            env.dispose();
         }
     }
 }

Facility problem

Currently, there are two shopping malls, and the locations of the malls have already been determined. There are several alternative locations for constructing warehouses, whose coordinates and construction costs are known. We assume that the transportation cost from the warehouses to the shopping malls is independent of the quantity of goods but is related to the distance between them. Please find the minimum cost scheme for warehouse construction and transportation.

Sets

  • Alternative Warehouses \(F\)

  • Shopping Malls \(M\)

Parameters

  • The transportation cost from warehouse \(i \in F\) to shopping mall \(j \in M\) is \(a_{ij}\) .

  • The cost for constructing warehouse \(i \in F\) is \(c_i\) .

  • The demand for goods at shopping mall \(j \in M\) is \(d_j\) .

Decision Variables

  • \(x_i\) indicates whether a warehouse is constructed at alternative location \(i \in F\) . It can take a value of 0 or 1, where 0 means not to build and 1 means to build.

  • \(y_{ij}\) represents the quantity of goods transported from warehouse \(i \in F\) to shopping mall \(j \in M\) .

Objective Function

Minimize the combined cost of warehouse construction and goods transportation:

\(\text{minimize}~ \sum_{i\in F} c_{i}x_i + \sum_{i\in F,j \in M} a_{ij}y_{ij}\)

Constraints

\(\sum_{i\in F} y_{ij} = d_{j}, ~~ \forall j\in M\)

\(x_i d_j - \sum_{k\in M} y_{ik} = 0, ~~ \forall i \in F, j \in M\)


import java.util.*;
// The objective of this example is to find the minimum cost solution for warehouse construction and transportation
public class ExampleFacility {
    public static void main(String[] args) throws MDOException {
        MDOEnv env = new MDOEnv();
        MDOModel model = new MDOModel(env);
        model.set(MDO.StringAttr.ModelName, "Facility");

        // There are two shopping malls with fixed locations at (0, 1.7) and (1.4, 2.9), requiring 100 and 200 units of goods weight.
        Map<List<Double>, Integer> marketInfo = new HashMap<>();
        marketInfo.put(Arrays.asList(0.0, 1.7), 100);
        marketInfo.put(Arrays.asList(1.4, 2.9), 200);
        List<List<Double>> marketKeys = new ArrayList<>();
        marketKeys.add(Arrays.asList(0.0, 1.7));
        marketKeys.add(Arrays.asList(1.4, 2.9));
        int marketNum = marketInfo.size();

        // Optional location and construction cost of warehouses
        Map<List<Integer>, Double> facilitiesInfo = new HashMap<>();
        facilitiesInfo.put(Arrays.asList(0, 1), 3.0);
        facilitiesInfo.put(Arrays.asList(0, 2), 1.0);
        facilitiesInfo.put(Arrays.asList(1, 0), 1.5);
        facilitiesInfo.put(Arrays.asList(1, 1), 1.3);
        facilitiesInfo.put(Arrays.asList(1, 2), 1.8);
        facilitiesInfo.put(Arrays.asList(2, 0), 1.6);
        facilitiesInfo.put(Arrays.asList(2, 1), 1.1);
        facilitiesInfo.put(Arrays.asList(2, 2), 1.9);
        List<List<Integer>> facilitiesKeys = new ArrayList<>();
        facilitiesKeys.add(Arrays.asList(0, 1));
        facilitiesKeys.add(Arrays.asList(0, 2));
        facilitiesKeys.add(Arrays.asList(1, 0));
        facilitiesKeys.add(Arrays.asList(1, 1));
        facilitiesKeys.add(Arrays.asList(1, 2));
        facilitiesKeys.add(Arrays.asList(2, 0));
        facilitiesKeys.add(Arrays.asList(2, 1));
        facilitiesKeys.add(Arrays.asList(2, 2));
        int facilitiesNum = facilitiesInfo.size();

        double transportFeePerM = 1.23;

        try {
            // Add Variables
            MDOVar[] xVars = model.addVars(facilitiesNum, MDO.BINARY);
            MDOVar[][] yVars = new MDOVar[marketNum][facilitiesNum];
            for (int i = 0; i < marketNum; i++) {
                for (int j = 0; j < facilitiesNum; j++) {
                    yVars[i][j] = model.addVar(0, MDO.INFINITY, 0, MDO.CONTINUOUS, String.valueOf(i) + String.valueOf(j));
                }
            }

            // Add Constraints
            for (int i = 0; i < marketNum; i++) {
            // Constraint 1 : ensure that all demand of the shopping centers is satisfied
                MDOLinExpr linExpr = new MDOLinExpr();
                for (int j = 0; j < facilitiesNum; j++) {
                    linExpr.addTerm(1, yVars[i][j]);
                    MDOLinExpr lhe = new MDOLinExpr();
                    lhe.addTerm(1.0 / marketInfo.get(marketKeys.get(i)), yVars[i][j]);
                    model.addConstr(lhe, MDO.LESS_EQUAL, xVars[j], "is_built[" + i + "," + j + "]");
                }
                // Constraint 2 : If this warehouse is not constructed, the amount of goods transported from this location must be 0
                model.addConstr(linExpr, MDO.EQUAL, marketInfo.get(marketKeys.get(i)), "is_satisfy_" + i);
            }

            // Add Objective Function: Minimize the sum of transportation costs and facility construction costs"。
            // Assume that the transportation cost from A to B depends only on the distance and is independent of the weight of the goods
            MDOLinExpr objective = new MDOLinExpr();
            for (int j = 0; j < facilitiesNum; j++) {
                objective.addTerm(facilitiesInfo.get(facilitiesKeys.get(j)), xVars[j]);
            }
            for (int j = 0; j < facilitiesNum; j++) {
                for (int i = 0; i < marketNum; i++) {
                    objective.addTerm(calculateTransportationFee(marketKeys.get(i), facilitiesKeys.get(j), transportFeePerM), yVars[i][j]);
                }
            }
            model.setObjective(objective, MDO.MINIMIZE);

            // Start Optimizing
            model.optimize();

            // Print Result
            for (int i = 0; i < facilitiesNum; i++) {
                MDOVar x = xVars[i];
                if (x.get(MDO.DoubleAttr.X) == 1) {
                    System.out.println("The No." + i + " warehouse should be built at (" + facilitiesKeys.get(i).get(0)
                    + ", " + facilitiesKeys.get(i).get(1) + ")");
                }
            }
            model.write("TestFacility.mps");

            System.out.println(objective.getValue());
        } catch (MDOException e) {
            System.out.println(e.getMessage());
        }finally {
            model.dispose();
            env.dispose();
        }
    }

    private static double calculateTransportationFee(List<Double> pos1, List<Integer> pos2, double transportFeePerM) {
        double x1 = pos1.get(0) - pos2.get(0);
        double x2 = pos1.get(1) - pos2.get(1);
        return Math.sqrt(x1 * x1 + x2 * x2) * transportFeePerM;
    }
}

WorkForce problem

In a week, the number of workers needed by the factory varies each day. It is currently known how much each worker earns per day and the dates on which they can attend work. The goal is to calculate how to schedule the workers in order to meet the factory’s operational requirements while minimizing wage costs.

Sets:

  • Days of the week \(D = 1, 2, 3, \ldots, 7\)

  • Number of workers needed by the factory \(N\)

  • Workers \(S\)

Parameters:

  • The number of workers needed by the factory on day \(i \in D\) is \(n_i\) .

  • The daily wage of worker \(j \in S\) is \(s_j\) .

  • A sequence indicating the available working times for workers, totaling \(T\) pairs of (worker, day) denoted as \((d_i, t_i)\) , where \(d_i \in S\) and \(t_i \in D\) , for \(i = 1, 2, 3, \ldots, T\) .

Objective Function:

Minimize the total wages paid: \(\text{minimize}~ \sum_{i=1}^{T} x_i s_{d_i}\)

Decision Variables:

  • \(x_i (i = 1, 2, 3, \ldots, T)\) indicates whether the worker in the available working time sequence shows up on that day. Its values must be 0 or 1, where 0 indicates the worker does not attend, and 1 indicates the worker does attend.

Constraints:

\(\sum_{d_i=r} x_i = n_{r}, ~~\forall r\in D\)


import java.util.*;

public class ExampleWorkforce {
    public static void main(String[] args) throws MDOException {
        MDOEnv env = new MDOEnv();
        MDOModel model = new MDOModel(env);

        // Number of required workers for each day
        String[] dayName = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
        int[] workersPerDay = {  3, 1, 4, 2, 1, 3, 3 };

        // Daily wage of each worker
        Map<String, Integer> workers = new HashMap<>();
        workers.put("Xiaoming", 13);
        workers.put("Huahua",   10);
        workers.put("HongHong", 11);
        workers.put("Dahua",    8);
        workers.put("Lihua",    9);
        workers.put("Niuniu",   14);
        workers.put("Gouzi",    14);
        List<String> workers_name = new ArrayList<>(workers.keySet());

        // Available days for each worker
        List<String[]> availability = new ArrayList<>();
        availability.add(new String[]{ "Xiaoming",   "Tuesday" });
        availability.add(new String[]{ "Xiaoming",   "Wednesday" });
        availability.add(new String[]{ "Xiaoming",   "Friday" });
        availability.add(new String[]{ "Xiaoming",   "Sunday" });
        availability.add(new String[]{ "Huahua",     "Monday" });
        availability.add(new String[]{ "Huahua",     "Tuesday" });
        availability.add(new String[]{ "Huahua",     "Friday" });
        availability.add(new String[]{ "Huahua",     "Saturday" });
        availability.add(new String[]{ "HongHong",   "Wednesday" });
        availability.add(new String[]{ "HongHong",   "Thursday" });
        availability.add(new String[]{ "HongHong",   "Friday" });
        availability.add(new String[]{ "HongHong",   "Sunday" });
        availability.add(new String[]{ "Dahua",      "Tuesday" });
        availability.add(new String[]{ "Dahua",      "Wednesday" });
        availability.add(new String[]{ "Dahua",      "Friday" });
        availability.add(new String[]{ "Dahua",      "Saturday" });
        availability.add(new String[]{ "Lihua",      "Monday" });
        availability.add(new String[]{ "Lihua",      "Tuesday" });
        availability.add(new String[]{ "Lihua",      "Wednesday" });
        availability.add(new String[]{ "Lihua",      "Thursday" });
        availability.add(new String[]{ "Lihua",      "Friday" });
        availability.add(new String[]{ "Lihua",      "Sunday" });
        availability.add(new String[]{ "Niuniu",     "Monday" });
        availability.add(new String[]{ "Niuniu",     "Tuesday" });
        availability.add(new String[]{ "Niuniu",     "Wednesday" });
        availability.add(new String[]{ "Niuniu",     "Saturday" });
        availability.add(new String[]{ "Gouzi",      "Monday" });
        availability.add(new String[]{ "Gouzi",      "Tuesday" });
        availability.add(new String[]{ "Gouzi",      "Wednesday" });
        availability.add(new String[]{ "Gouzi",      "Friday" });
        availability.add(new String[]{ "Gouzi",      "Saturday" });
        availability.add(new String[]{ "Gouzi",      "Sunday" });

        try {
            // Add Variables
            // x[(worker, day)] represents whether this worker is scheduled for this day.
            // Using worker-day pair to initialize variables ensure that each person works only at the time they are available
            MDOVar[] x = new MDOVar[availability.size()];
            for (int i = 0; i < availability.size(); i++) {
                String[] worker_day = availability.get(i);
                x[i] = model.addVar(0, 1, 0, MDO.BINARY, "schedule_" + worker_day[0] + "," + worker_day[1]);
            }

            // Add Constraints
            // Constraint : ensure that each day has enough workforce
            Map<String, MDOLinExpr> day_count = new HashMap<>();
            for (String day : dayName) {
                MDOLinExpr le = new MDOLinExpr();
                day_count.put(day, le);
            }
            for (int i = 0; i < availability.size(); i++) {
                MDOLinExpr expr = new MDOLinExpr();
                String[] worker_day = availability.get(i);
                day_count.get(worker_day[1]).addTerm(1, x[i]);
            }
            for (int i = 0; i < dayName.length; i++) {
                MDOLinExpr expr = day_count.get(dayName[i]);
                model.addConstr(expr, MDO.EQUAL, workersPerDay[i], "c1" + availability.get(i)[1]);
            }

            // Add Objective Function
            MDOLinExpr obj = new MDOLinExpr();
            for (int i = 0; i < availability.size(); i++) {
                obj.addTerm(workers.get(availability.get(i)[0]), x[i]);
            }
            model.setObjective(obj, MDO.MINIMIZE);

            // Start Optimizing
            model.optimize();

            // Print Result
            for (int i = 0; i < availability.size(); i++) {
                if (x[i].get(MDO.DoubleAttr.X) > 0) {
                    System.out.println(availability.get(i)[0] + " should work at " + availability.get(i)[1]);
                }
            }
            System.out.println("The total cost is " + model.get(MDO.DoubleAttr.ObjVal));
        } catch (Exception e) {
            System.out.println("Exception during optimization");
            e.printStackTrace();
        } finally {
            model.dispose();
            env.dispose();
        }
    }
}