diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index fb3fea1f3a..9799abf2ed 100644 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -275,6 +275,11 @@ test_java_linear_solver_samples: \ rjava_SimpleMipProgram \ rjava_StiglerDiet +.PHONY: test_java_model_builder_samples # Build and Run all Java MB Samples (located in ortools/model_builder/samples) +test_java_model_builder_samples: \ + rjava_SimpleLpProgramMb \ + rjava_SimpleMipProgramMb + .PHONY: test_java_sat_samples # Build and Run all Java SAT Samples (located in ortools/sat/samples) test_java_sat_samples: \ rjava_AssignmentSat \ @@ -305,6 +310,7 @@ check_java: \ test_java_constraint_solver_samples \ test_java_graph_samples \ test_java_linear_solver_samples \ + test_java_model_builder_samples \ test_java_sat_samples \ \ rjava_LinearProgramming \ diff --git a/ortools/java/com/google/ortools/modelbuilder/LinearConstraint.java b/ortools/java/com/google/ortools/modelbuilder/LinearConstraint.java index 198716f134..bf20ffc2fc 100644 --- a/ortools/java/com/google/ortools/modelbuilder/LinearConstraint.java +++ b/ortools/java/com/google/ortools/modelbuilder/LinearConstraint.java @@ -35,36 +35,36 @@ public class LinearConstraint { return helper.getConstraintLowerBound(index); } - /** Returns the upper bound of the variable. */ - public double getUpperBound() { - return helper.getConstraintUpperBound(index); - } - - /** Returns the name of the variable given upon creation. */ - public String getName() { - return helper.getConstraintName(index); - } - /** Returns the lower bound of the variable. */ public void setLowerBound(double lb) { helper.setConstraintLowerBound(index, lb); } + /** Returns the upper bound of the variable. */ + public double getUpperBound() { + return helper.getConstraintUpperBound(index); + } + /** Returns the upper bound of the variable. */ public void setUpperBound(double ub) { helper.setConstraintUpperBound(index, ub); } + /** Returns the name of the variable given upon creation. */ + public String getName() { + return helper.getConstraintName(index); + } + + // Sets the name of the variable. */ + public void setName(String name) { + helper.setConstraintName(index, name); + } + // Adds var * coeff to the constraint. public void addTerm(Variable var, double coeff) { helper.addConstraintTerm(index, var.getIndex(), coeff); } - /** Returns the name of the variable given upon creation. */ - public void setName(String name) { - helper.setConstraintName(index, name); - } - /** Inline setter */ public LinearConstraint withName(String name) { setName(name); diff --git a/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java b/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java index 158037f6fe..71850b5eaa 100644 --- a/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java +++ b/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java @@ -184,6 +184,18 @@ public final class ModelBuilder { helper.setMaximize(maximize); } + // Model getters, import, export. + + /** Returns the name of the model. */ + public String getName() { + return helper.getName(); + } + + /** Sets the name of the model. */ + public void setName(String name) { + helper.setName(name); + } + /** * Write the model as a protocol buffer to 'file'. * @@ -191,10 +203,34 @@ public final class ModelBuilder { * written as a text file, otherwise, the binary format will be used. * @return true if the model was correctly written. */ - public Boolean exportToFile(String file) { + public boolean exportToFile(String file) { return helper.writeModelToFile(file); } + public String exportToMpsString(boolean obfuscate) { + return helper.exportToMpsString(obfuscate); + } + + public String exportToLpString(boolean obfuscate) { + return helper.exportToLpString(obfuscate); + } + + public boolean importFromMpsString(String mpsString) { + return helper.importFromMpsString(mpsString); + } + + public boolean importFromMpsFile(String mpsFile) { + return helper.importFromMpsString(mpsFile); + } + + public boolean importFromLpString(String lpString) { + return helper.importFromLpString(lpString); + } + + public boolean importFromLpFile(String lpFile) { + return helper.importFromMpsString(lpFile); + } + // Getters. /** Returns the model builder helper. */ public ModelBuilderHelper getHelper() { diff --git a/ortools/java/com/google/ortools/modelbuilder/ModelSolver.java b/ortools/java/com/google/ortools/modelbuilder/ModelSolver.java index 1edc1e10d9..315d043bc9 100644 --- a/ortools/java/com/google/ortools/modelbuilder/ModelSolver.java +++ b/ortools/java/com/google/ortools/modelbuilder/ModelSolver.java @@ -13,6 +13,8 @@ package com.google.ortools.modelbuilder; +import java.util.function.Consumer; + /** Model solver class */ public final class ModelSolver { static class ModelSolverException extends RuntimeException { @@ -22,11 +24,15 @@ public final class ModelSolver { } } + /** Creates the solver with the supplied solver backend. */ public ModelSolver(String solverName) { this.helper = new ModelSolverHelper(solverName); + this.logCallback = null; } + /** Solves given model, and returns the status of the response. */ public SolveStatus solve(ModelBuilder model) { + helper.setLogCallback(logCallback); helper.solve(model.getHelper()); if (!helper.hasResponse()) { return SolveStatus.UNKNOWN_STATUS; @@ -34,25 +40,100 @@ public final class ModelSolver { return helper.getStatus(); } - public double objectiveValue() { + /** Enables or disables the underlying solver output. */ + public void enableOutput(boolean enable) { + helper.enableOutput(enable); + } + + /** Sets the time limit for the solve in seconds. */ + public void setTimeLimitInSeconds(double limit) { + helper.setTimeLimitInSeconds(limit); + } + + /** Sets solver specific parameters as string. */ + public void setSolverSpecificParameters(String parameters) { + helper.setSolverSpecificParameters(parameters); + } + + /** Returns whether solver specified during the ctor was found and correctly installed. */ + public boolean solverIsSupported() { + return helper.solverIsSupported(); + } + + /** Tries to interrupt the solve. Returns true if the feature is supported. */ + public boolean interruptSolve() { + return helper.interruptSolve(); + } + + /** Returns true if solve() was called, and a response was returned. */ + public boolean hasResponse() { + return helper.hasResponse(); + } + + /** Returns true if solve() was called, and a solution was returned. */ + public boolean hasSolution() { + return helper.hasSolution(); + } + + /** Checks that the solver has found a solution, and returns the objective value. */ + public double getObjectiveValue() { if (!helper.hasSolution()) { throw new ModelSolverException( - "ModelSolver.objectiveValue()", "solve() was not called or no solution was found"); + "ModelSolver.getObjectiveValue()", "solve() was not called or no solution was found"); } return helper.getObjectiveValue(); } - public double value(Variable var) { + /** Checks that the solver has found a solution, and returns the objective value. */ + public double getBestObjectiveBound() { if (!helper.hasSolution()) { throw new ModelSolverException( - "ModelSolver.value())", "solve() was not called or no solution was found"); + "ModelSolver.getBestObjectiveBound()", "solve() was not called or no solution was found"); + } + return helper.getBestObjectiveBound(); + } + + /** Checks that the solver has found a solution, and value of the given variable. */ + public double getValue(Variable var) { + if (!helper.hasSolution()) { + throw new ModelSolverException( + "ModelSolver.getValue())", "solve() was not called or no solution was found"); } return helper.getVariableValue(var.getIndex()); } + /** Checks that the solver has found a solution, and reduced cost of the given variable. */ + public double getReducedCost(Variable var) { + if (!helper.hasSolution()) { + throw new ModelSolverException( + "ModelSolver.getReducedCost())", "solve() was not called or no solution was found"); + } + return helper.getReducedCost(var.getIndex()); + } - public double wallTime() { + /** Checks that the solver has found a solution, and dual value of the given constraint. */ + public double getDualValue(LinearConstraint ct) { + if (!helper.hasSolution()) { + throw new ModelSolverException( + "ModelSolver.getDualValue())", "solve() was not called or no solution was found"); + } + return helper.getDualValue(ct.getIndex()); + } + + /** Sets the log callback for the solver. */ + public void setLogCallback(Consumer cb) { + this.logCallback = cb; + } + + /** Returns the elapsed time since the creation of the solver. */ + public double getWallTime() { return helper.getWallTime(); } - ModelSolverHelper helper; + /** Returns the user time since the creation of the solver. */ + public double getUserTime() { + return helper.getUserTime(); + } + + private final ModelSolverHelper helper; + private Consumer logCallback; } diff --git a/ortools/java/com/google/ortools/modelbuilder/Variable.java b/ortools/java/com/google/ortools/modelbuilder/Variable.java index e328e74819..019a80ccb7 100644 --- a/ortools/java/com/google/ortools/modelbuilder/Variable.java +++ b/ortools/java/com/google/ortools/modelbuilder/Variable.java @@ -45,14 +45,39 @@ public class Variable implements LinearArgument { return helper.getVarLowerBound(index); } + /** Sets the lower bound of the variable. */ + public void setLowerBound(double lowerBound) { + helper.setVarLowerBound(index, lowerBound); + } + /** Returns the upper bound of the variable. */ public double getUpperBound() { return helper.getVarUpperBound(index); } + /** Sets the upper bound of the variable. */ + public void setUpperBound(double upperBound) { + helper.setVarUpperBound(index, upperBound); + } + /** Returns whether the variable is integral. */ - public boolean isIntegral() { - return helper.getVarIsIntegral(index); + public boolean getVarIntegrality() { + return helper.getVarIntegrality(index); + } + + /** Sets the integrality of the variable. */ + public void setVarIntegrality(boolean isIntegral) { + helper.setVarIntegrality(index, isIntegral); + } + + /** Returns the objective coefficient of the variable. */ + public double getObjectiveCoefficient() { + return helper.getVarObjectiveCoefficient(index); + } + + /** Sets the objective coefficient of the variable in the objective. */ + public void setObjectiveCoefficient(double objectiveCoefficient) { + helper.setVarObjectiveCoefficient(index, objectiveCoefficient); } /** Returns the name of the variable given upon creation. */ @@ -60,9 +85,8 @@ public class Variable implements LinearArgument { return helper.getVarName(index); } - /** Returns the objective coefficient of the variable. */ - public double getObjectiveCoefficient() { - return helper.getVarObjectiveCoefficient(index); + public void setName(String name) { + helper.setVarName(index, name); } @Override diff --git a/ortools/model_builder/java/modelbuilder.i b/ortools/model_builder/java/modelbuilder.i index 4ded30e9c5..a41fbbd808 100644 --- a/ortools/model_builder/java/modelbuilder.i +++ b/ortools/model_builder/java/modelbuilder.i @@ -29,6 +29,27 @@ // TODO(user): cleanup java/functions.i and move the code there. %{ #include // std::make_shared + +/* Global JNI reference deleter. Instantiate it via std::make_shared<> */ +class GlobalRefGuard { + JavaVM *jvm_; + jobject jref_; + // non-copyable + GlobalRefGuard(const GlobalRefGuard &) = delete; + GlobalRefGuard &operator=(const GlobalRefGuard &) = delete; + public: + GlobalRefGuard(JavaVM *jvm, jobject jref): jvm_(jvm), jref_(jref) {} + ~GlobalRefGuard() { + JNIEnv *jenv = NULL; + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_2; + args.name = NULL; + args.group = NULL; + jvm_->AttachCurrentThread((void**)&jenv, &args); + jenv->DeleteGlobalRef(jref_); + jvm_->DetachCurrentThread(); + } +}; %} %typemap(in) std::function %{ @@ -65,6 +86,20 @@ %typemap(jstype) std::function "java.util.function.Consumer" // Type used in the Proxy class. %typemap(javain) std::function "$javainput" // passing the Callback to JNI java class. +%extend operations_research::ModelBuilderHelper { + std::string exportToMpsString(bool obfuscate) { + operations_research::MPModelExportOptions options; + options.obfuscate = obfuscate; + return $self->ExportToMpsString(options); + } + + std::string exportToLpString(bool obfuscate) { + operations_research::MPModelExportOptions options; + options.obfuscate = obfuscate; + return $self->ExportToLpString(options); + } +} // Extend operations_research::ModelBuilderHelper + %ignoreall %unignore operations_research; @@ -76,7 +111,7 @@ // Var API. %rename (addVar) operations_research::ModelBuilderHelper::AddVar; -%rename (getVarIsIntegral) operations_research::ModelBuilderHelper::VarIsIntegral; +%rename (getVarIntegrality) operations_research::ModelBuilderHelper::VarIsIntegral; %rename (getVarLowerBound) operations_research::ModelBuilderHelper::VarLowerBound; %rename (getVarName) operations_research::ModelBuilderHelper::VarName; %rename (getVarObjectiveCoefficient) operations_research::ModelBuilderHelper::VarObjectiveCoefficient; @@ -116,11 +151,14 @@ %rename (importFromMpsFile) operations_research::ModelBuilderHelper::ImportFromMpsFile; %rename (importFromLpString) operations_research::ModelBuilderHelper::ImportFromLpString; %rename (importFromLpFile) operations_research::ModelBuilderHelper::ImportFromLpFile; +%unignore operations_research::ModelBuilderHelper::exportToMpsString; +%unignore operations_research::ModelBuilderHelper::exportToLpString; %unignore operations_research::ModelSolverHelper; %unignore operations_research::ModelSolverHelper::ModelSolverHelper(const std::string&); -%rename (solverIsSupported) operations_research::ModelSolverHelper::solverIsSupported; +%rename (solverIsSupported) operations_research::ModelSolverHelper::SolverIsSupported; %rename (solve) operations_research::ModelSolverHelper::Solve; +%rename (interruptSolve) operations_research::ModelSolverHelper::InterruptSolve; %rename (hasResponse) operations_research::ModelSolverHelper::has_response; %rename (hasSolution) operations_research::ModelSolverHelper::has_solution; %rename (getStatus) operations_research::ModelSolverHelper::status; @@ -131,7 +169,11 @@ %rename (getDualValue) operations_research::ModelSolverHelper::dual_value; %rename (getStatusString) operations_research::ModelSolverHelper::status_string; %rename (getWallTime) operations_research::ModelSolverHelper::wall_time; +%rename (getUserTime) operations_research::ModelSolverHelper::user_time; %rename (enableOutput) operations_research::ModelSolverHelper::EnableOutput; +%rename (setLogCallback) operations_research::ModelSolverHelper::SetLogCallback; +%rename (setTimeLimitInSeconds) operations_research::ModelSolverHelper::SetTimeLimitInSeconds; +%rename (setSolverSpecificParameters) operations_research::ModelSolverHelper::SetSolverSpecificParameters; %unignore operations_research::SolveStatus; %unignore operations_research::OPTIMAL; diff --git a/ortools/model_builder/samples/SimpleLpProgramMb.java b/ortools/model_builder/samples/SimpleLpProgramMb.java index fe5da44e6d..20a18e8b1d 100644 --- a/ortools/model_builder/samples/SimpleLpProgramMb.java +++ b/ortools/model_builder/samples/SimpleLpProgramMb.java @@ -66,9 +66,9 @@ public final class SimpleLpProgramMb { // [START print_solution] if (status == SolveStatus.OPTIMAL) { System.out.println("Solution:"); - System.out.println("Objective value = " + solver.objectiveValue()); - System.out.println("x = " + solver.value(x)); - System.out.println("y = " + solver.value(y)); + System.out.println("Objective value = " + solver.getObjectiveValue()); + System.out.println("x = " + solver.getValue(x)); + System.out.println("y = " + solver.getValue(y)); } else { System.err.println("The problem does not have an optimal solution!"); } @@ -76,7 +76,7 @@ public final class SimpleLpProgramMb { // [START advanced] System.out.println("\nAdvanced usage:"); - System.out.println("Problem solved in " + solver.wallTime() + " milliseconds"); + System.out.println("Problem solved in " + solver.getWallTime() + " seconds"); // [END advanced] } diff --git a/ortools/model_builder/samples/SimpleMipProgramMb.java b/ortools/model_builder/samples/SimpleMipProgramMb.java index da6a86b931..95932f875e 100644 --- a/ortools/model_builder/samples/SimpleMipProgramMb.java +++ b/ortools/model_builder/samples/SimpleMipProgramMb.java @@ -65,9 +65,9 @@ public final class SimpleMipProgramMb { // [START print_solution] if (status == SolveStatus.OPTIMAL) { System.out.println("Solution:"); - System.out.println("Objective value = " + solver.objectiveValue()); - System.out.println("x = " + solver.value(x)); - System.out.println("y = " + solver.value(y)); + System.out.println("Objective value = " + solver.getObjectiveValue()); + System.out.println("x = " + solver.getValue(x)); + System.out.println("y = " + solver.getValue(y)); } else { System.err.println("The problem does not have an optimal solution!"); } @@ -75,7 +75,8 @@ public final class SimpleMipProgramMb { // [START advanced] System.out.println("\nAdvanced usage:"); - System.out.println("Problem solved in " + solver.wallTime() + " milliseconds"); + System.out.println("Problem solved in " + solver.getWallTime() + " seconds"); + // [END advanced] } private SimpleMipProgramMb() {}