Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
import java.util.Iterator;
import java.util.List;

/**
* ExcelReader class that reads the input from an Excel file.
* It expects two sheets: one for items ('items') and one for
* knapsacks ('knapsacks'). Each sheet should have a header row.
*/
public class ExcelReader {

public Input readExcelFile(String filePath) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,19 @@
import java.io.IOException;
import java.util.List;

/**
* ExcelWriter class to write the solution to an Excel file.
* It creates two sheets: one for assignments and one for unassigned items.
*/
public class ExcelWriter {

/**
* Writes the solution to an Excel file.
*
* @param solution The solution containing assignments and unassigned items.
* @param filePath The path where the Excel file will be saved.
* @throws IOException If an I/O error occurs while writing the file.
*/
public void writeSolutionToExcel(Solution solution, String filePath) throws IOException {
Workbook workbook = new XSSFWorkbook();

Expand Down Expand Up @@ -56,7 +67,10 @@ private void createUnassignedSheet(Sheet sheet, List<String> unassigned) {
}
}

// Assuming these classes are defined as follows:
/**
* Represents a solution containing assignments of items to knapsacks
* and a list of unassigned items.
*/
class Solution {
private List<Assignment> assignments;
private List<String> unassigned;
Expand All @@ -75,6 +89,9 @@ public List<String> getUnassigned() {
}
}

/**
* Represents an assignment of an item to a knapsack.
*/
class Assignment {
private String itemId;
private String knapsackId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

public final class Main {

/**
* Main entry point for the application.
*
* @param args Command line arguments.
*/
public static void main(String[] args) {
try {
// Parse arguments.
Expand Down Expand Up @@ -43,7 +48,7 @@ public static void main(String[] args) {

// Setup Gurobi environment and model.
GRBEnv env = new GRBEnv(true);
env.set("OutputFlag", "0"); // Disable output if needed
// env.set("OutputFlag", "0"); // Disable output if needed
env.start();
GRBModel model = new GRBModel(env);

Expand All @@ -53,9 +58,10 @@ public static void main(String[] args) {
// Create assignment variable for each item in each knapsack.
// Variables are binary, indicating whether an item is assigned to a knapsack.
List<GRBVar> variables = new ArrayList<>();
List<Item> inputItems = input.getItems();
for (Knapsack knapsack : input.getKnapsacks()) {
for (Item item : inputItems) {
List<Item> items = input.getItems();
List<Knapsack> knapsacks = input.getKnapsacks();
for (Knapsack knapsack : knapsacks) {
for (Item item : items) {
variables.add(model.addVar(0.0, 1.0, 0.0, GRB.BINARY, knapsack.getId() + "_" + item.getId()));
}
}
Expand All @@ -64,29 +70,29 @@ public static void main(String[] args) {
model.update();

// Create capacity constraint.
for (int i = 0; i < input.getKnapsacks().size(); ++i) {
Knapsack knapsack = input.getKnapsacks().get(i);
for (int i = 0; i < knapsacks.size(); ++i) {
Knapsack knapsack = knapsacks.get(i);
GRBLinExpr knapsackExpr = new GRBLinExpr();
for (int j = 0; j < inputItems.size(); ++j) {
knapsackExpr.addTerm(inputItems.get(j).getWeight(), variables.get(i * inputItems.size() + j));
for (int j = 0; j < items.size(); ++j) {
knapsackExpr.addTerm(items.get(j).getWeight(), variables.get(i * items.size() + j));
}
model.addConstr(knapsackExpr, GRB.LESS_EQUAL, knapsack.getCapacity(), "capacity_" + knapsack.getId());
}

// Ensure that each item can only be assigned once.
for (int j = 0; j < inputItems.size(); ++j) {
for (int j = 0; j < items.size(); ++j) {
GRBLinExpr itemExpr = new GRBLinExpr();
for (int i = 0; i < input.getKnapsacks().size(); ++i) {
itemExpr.addTerm(1.0, variables.get(i * inputItems.size() + j));
for (int i = 0; i < knapsacks.size(); ++i) {
itemExpr.addTerm(1.0, variables.get(i * items.size() + j));
}
model.addConstr(itemExpr, GRB.LESS_EQUAL, 1.0, "item_assignment_" + inputItems.get(j).getId());
model.addConstr(itemExpr, GRB.LESS_EQUAL, 1.0, "item_assignment_" + items.get(j).getId());
}

// Create the objective function.
GRBLinExpr objectiveExpr = new GRBLinExpr();
for (int i = 0; i < input.getKnapsacks().size(); ++i) {
for (int j = 0; j < inputItems.size(); ++j) {
objectiveExpr.addTerm(inputItems.get(j).getValue(), variables.get(i * inputItems.size() + j));
for (int i = 0; i < knapsacks.size(); ++i) {
for (int j = 0; j < items.size(); ++j) {
objectiveExpr.addTerm(items.get(j).getValue(), variables.get(i * items.size() + j));
}
}
model.setObjective(objectiveExpr, GRB.MAXIMIZE);
Expand All @@ -98,13 +104,13 @@ public static void main(String[] args) {
List<Assignment> assignments = new ArrayList<>();
Set<String> unassignedItems = new HashSet<>();
for (int i = 0; i < variables.size(); ++i) {
int knapsackIndex = i / inputItems.size();
int itemIndex = i % inputItems.size();
int knapsackIndex = i / items.size();
int itemIndex = i % items.size();
if (variables.get(i).get(GRB.DoubleAttr.X) > 0.5) {
assignments
.add(new Assignment(inputItems.get(itemIndex).getId(), input.getKnapsacks().get(knapsackIndex).getId()));
.add(new Assignment(items.get(itemIndex).getId(), knapsacks.get(knapsackIndex).getId()));
} else {
unassignedItems.add(inputItems.get(itemIndex).getId());
unassignedItems.add(items.get(itemIndex).getId());
}
}
Solution solution = new Solution(assignments, new ArrayList<>(unassignedItems));
Expand All @@ -125,7 +131,11 @@ public static void main(String[] args) {
"Gurobi",
convertStatus(model.get(GRB.IntAttr.Status)),
model.get(GRB.IntAttr.NumVars),
model.get(GRB.IntAttr.NumConstrs));
model.get(GRB.IntAttr.NumConstrs),
items.size(),
knapsacks.size(),
assignments.size(),
unassignedItems.size());

// Write output.
Output.write(output, options.getOutputPath());
Expand All @@ -140,6 +150,11 @@ public static void main(String[] args) {
}
}

/**
* Prepares the output directory by creating necessary subdirectories.
*
* @param outputPath The path to the output directory.
*/
private static void prepareOutputDirectory(String outputPath) {
// Prepare output directory if it does not exist.
Path solutionsPath = Paths.get(outputPath, "solutions");
Expand All @@ -164,6 +179,12 @@ private static void prepareOutputDirectory(String outputPath) {
}
}

/**
* Converts Gurobi status codes to human-readable strings.
*
* @param status The Gurobi status code.
* @return A string representation of the status.
*/
public static String convertStatus(int status) {
switch (status) {
case GRB.Status.LOADED:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
package com.nextmv.example;

import java.util.List;
import java.util.ArrayList;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.google.gson.Gson;

/**
* Output class that wraps the result of the optimization run.
* Since this app outputs non-JSON data, the output only contains
* the statistics of the run. The solution is written to separate
* files in the output directory.
*/
public class Output {
/**
* StatisticsRun contains more generic metrics about the run.
*/
private final class StatisticsRun {
private double duration;
}

/**
* StatisticsResult contains metrics about the optimization result.
*/
private final class StatisticsResult {
private double value;
private StatisticsResultCustom custom;
}

/**
* StatisticsResultCustom contains custom metrics about the optimization
* result.
*/
private final class StatisticsResultCustom {
private String provider;
private String status;
private int variables;
private int constraints;
private int items;
private int knapsacks;
private int assigned;
private int unassigned;
}

/**
* Statistics is the root object for the metrics/statistics.
*/
private final class Statistics {
private String schema = "v1";
private StatisticsRun run;
private StatisticsResult result;
}

/**
* The statistics object that contains all the metrics.
*/
private final Statistics statistics;

public Output(
Expand All @@ -38,7 +62,11 @@ public Output(
String provider,
String status,
int variables,
int constraints) {
int constraints,
int items,
int knapsacks,
int assigned,
int unassigned) {
this.statistics = new Statistics();
this.statistics.run = new StatisticsRun();
this.statistics.run.duration = duration;
Expand All @@ -49,6 +77,10 @@ public Output(
this.statistics.result.custom.status = status;
this.statistics.result.custom.constraints = constraints;
this.statistics.result.custom.variables = variables;
this.statistics.result.custom.items = items;
this.statistics.result.custom.knapsacks = knapsacks;
this.statistics.result.custom.assigned = assigned;
this.statistics.result.custom.unassigned = unassigned;
}

public static void write(Output output, String outputPath) {
Expand Down
Loading