diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/Constant.java b/org.emoflon.ilp/src/org/emoflon/ilp/Constant.java index 25848d0..6e9d6de 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/Constant.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/Constant.java @@ -2,8 +2,7 @@ /** * Record class representing constant values. - * */ public record Constant(double weight) { -} \ No newline at end of file +} diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/Constraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/Constraint.java index 686ac41..80362dd 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/Constraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/Constraint.java @@ -1,5 +1,30 @@ package org.emoflon.ilp; -public interface Constraint { - // empty +/** + * Abstract class for all constraints. + */ +public abstract class Constraint { + /** + * The name of the constraint. + */ + private String name; + + /** + * Returns the name of the constraint. + * + * @return Name of the constraint. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the constraint to a given value. + * + * @param name New name of the constraint to set. + */ + public void setName(final String name) { + this.name = name; + } + } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/CplexSolver.java b/org.emoflon.ilp/src/org/emoflon/ilp/CplexSolver.java index 30293c9..b0a0c9c 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/CplexSolver.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/CplexSolver.java @@ -50,28 +50,28 @@ private void init() { // set configuration parameters // Presolve? - cplex.setParam(IloCplex.Param.Preprocessing.Presolve, config.presolveEnabled()); + cplex.setParam(IloCplex.Param.Preprocessing.Presolve, config.isPresolveEnabled()); // Random Seed? - if (config.randomSeedEnabled()) { - cplex.setParam(IloCplex.Param.RandomSeed, config.randomSeed()); + if (config.isRandomSeedEnabled()) { + cplex.setParam(IloCplex.Param.RandomSeed, config.getRandomSeed()); } // Output? - if (!config.debugOutputEnabled()) { + if (!config.isDebugOutputEnabled()) { cplex.setOut(null); } // Tolerance? - if (config.toleranceEnabled()) { - cplex.setParam(IloCplex.Param.MIP.Tolerances.Integrality, config.tolerance()); - cplex.setParam(IloCplex.Param.MIP.Tolerances.AbsMIPGap, config.tolerance()); + if (config.isToleranceEnabled()) { + cplex.setParam(IloCplex.Param.MIP.Tolerances.Integrality, config.getTolerance()); + cplex.setParam(IloCplex.Param.MIP.Tolerances.AbsMIPGap, config.getTolerance()); } // Timeout? - if (config.timeoutEnabled()) { - cplex.setParam(IloCplex.Param.TimeLimit, config.timeout()); + if (config.isTimeoutEnabled()) { + cplex.setParam(IloCplex.Param.TimeLimit, config.getTimeout()); } // set output path, if configured - if (config.outputEnabled()) { - this.outputPath = config.outputPath(); + if (config.isOutputEnabled()) { + this.outputPath = config.getOutputPath(); } } catch (final IloException e) { throw new RuntimeException(e); @@ -84,6 +84,7 @@ private void init() { @Override public void buildILPProblem(Problem problem) { this.problem = problem; + problem.validateConstraints(); /* * // Quadratic Constraints or Functions are not yet implemented if @@ -183,13 +184,13 @@ private IloIntVar translateIntegerVariable(IntegerVariable integerVariable) { int lb = integerVariable.getLowerBound(); int ub = integerVariable.getUpperBound(); - if (config.boundsEnabled()) { + if (config.isBoundsEnabled()) { if (integerVariable.isDefaultLowerBound()) { - lb = config.lowerBound(); + lb = config.getLowerBound(); integerVariable.setLowerBound((int) lb); } if (integerVariable.isDefaultUpperBound()) { - ub = config.upperBound(); + ub = config.getUpperBound(); integerVariable.setUpperBound((int) ub); } } @@ -216,13 +217,13 @@ private IloNumVar translateRealVariable(RealVariable realVariable) { double lb = realVariable.getLowerBound(); double ub = realVariable.getUpperBound(); - if (config.boundsEnabled()) { + if (config.isBoundsEnabled()) { if (realVariable.isDefaultLowerBound()) { - lb = config.lowerBound(); + lb = config.getLowerBound(); realVariable.setLowerBound(lb); } if (realVariable.isDefaultUpperBound()) { - ub = config.upperBound(); + ub = config.getUpperBound(); realVariable.setUpperBound(ub); } } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/GeneralConstraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/GeneralConstraint.java index 3a020d4..01e50d8 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/GeneralConstraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/GeneralConstraint.java @@ -2,48 +2,51 @@ import java.util.List; -// Class that is currently only used for Gurobi OrConstraints -// Could be extended for other general constraints (min, max, ...) -public interface GeneralConstraint extends Constraint { +/** + * Class that is currently only used for Gurobi OrConstraints. Could be extended + * for other general constraints (min, max, ...). + */ +public abstract class GeneralConstraint extends Constraint { /** * Returns all variables that are part of the constraint. * * @return List of variables. */ - public List> getVariables(); + public abstract List> getVariables(); /** * Sets the variables for this constraint. * * @param variables List of variables. */ - public void setVariables(List> variables); + public abstract void setVariables(List> variables); /** * Adds one variable to the list of variables. * * @param var New variable to be added. */ - public void addVariable(Variable var); + public abstract void addVariable(Variable var); /** * Returns the result variable of the constraint. * * @return Result variable. */ - public Variable getResult(); + public abstract Variable getResult(); /** * Sets the result variable of the constraint. * * @param res New result variable. */ - public void setResult(Variable res); + public abstract void setResult(Variable res); /** * Returns the type of the constraint. * * @return Type of the constraint. */ - public ConstraintType getType(); + public abstract ConstraintType getType(); + } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/GlpkSolver.java b/org.emoflon.ilp/src/org/emoflon/ilp/GlpkSolver.java index 8801b35..30b311e 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/GlpkSolver.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/GlpkSolver.java @@ -51,28 +51,28 @@ private void init() { GLPK.glp_init_iocp(iocp); // Set configuration parameters // Presolve? - iocp.setPresolve(config.presolveEnabled() ? GLPK.GLP_ON : GLPK.GLP_OFF); + iocp.setPresolve(config.isPresolveEnabled() ? GLPK.GLP_ON : GLPK.GLP_OFF); // Random Seed? // not supported in glpk for Java // --seed value is option fo glpsol // Debug Output? - if (!config.debugOutputEnabled()) { + if (!config.isDebugOutputEnabled()) { GLPK.glp_term_out(GLPK.GLP_OFF); } // Output? - if (config.outputEnabled()) { - this.outputPath = config.outputPath(); + if (config.isOutputEnabled()) { + this.outputPath = config.getOutputPath(); } // Tolerance? - if (config.toleranceEnabled()) { - iocp.setTol_int(config.tolerance()); - iocp.setTol_obj(config.tolerance()); - iocp.setMip_gap(config.tolerance()); + if (config.isToleranceEnabled()) { + iocp.setTol_int(config.getTolerance()); + iocp.setTol_obj(config.getTolerance()); + iocp.setMip_gap(config.getTolerance()); } // Timeout? - if (config.timeoutEnabled()) { + if (config.isTimeoutEnabled()) { // GLPK expects milliseconds - iocp.setTm_lim((int) config.timeout() * 1000); + iocp.setTm_lim((int) config.getTimeout() * 1000); } } @@ -80,6 +80,7 @@ private void init() { @Override public void buildILPProblem(Problem problem) { this.problem = problem; + problem.validateConstraints(); // Quadratic Constraints or Functions are not supported by GLPK if (problem.getConstraints().stream().anyMatch(QuadraticConstraint.class::isInstance) @@ -139,13 +140,13 @@ private void translateVariables(Map> vars) { break; case INTEGER: // Check if other bounds are defined in the solver config - if (config.boundsEnabled()) { + if (config.isBoundsEnabled()) { if (((IntegerVariable) var).isDefaultLowerBound()) { - lb = config.lowerBound(); + lb = config.getLowerBound(); ((IntegerVariable) var).setLowerBound((int) lb); } if (((IntegerVariable) var).isDefaultUpperBound()) { - ub = config.upperBound(); + ub = config.getUpperBound(); ((IntegerVariable) var).setUpperBound((int) ub); } } @@ -153,13 +154,13 @@ private void translateVariables(Map> vars) { break; case REAL: // Check if other bounds are defined in the solver config - if (config.boundsEnabled()) { + if (config.isBoundsEnabled()) { if (((RealVariable) var).isDefaultLowerBound()) { - lb = config.lowerBound(); + lb = config.getLowerBound(); ((RealVariable) var).setLowerBound(lb); } if (((RealVariable) var).isDefaultUpperBound()) { - ub = config.upperBound(); + ub = config.getUpperBound(); ((RealVariable) var).setUpperBound(ub); } } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/GurobiSolver.java b/org.emoflon.ilp/src/org/emoflon/ilp/GurobiSolver.java index e885562..1a91f0c 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/GurobiSolver.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/GurobiSolver.java @@ -57,31 +57,31 @@ private void init() throws GRBException { // set configuration parameters // Presolve? - env.set(IntParam.Presolve, config.presolveEnabled() ? 1 : 0); + env.set(IntParam.Presolve, config.isPresolveEnabled() ? 1 : 0); // Random Seed? - if (config.randomSeedEnabled()) { - env.set(IntParam.Seed, config.randomSeed()); + if (config.isRandomSeedEnabled()) { + env.set(IntParam.Seed, config.getRandomSeed()); } // Output? - if (!config.debugOutputEnabled()) { + if (!config.isDebugOutputEnabled()) { env.set(IntParam.OutputFlag, 0); } // Tolerance? - if (config.toleranceEnabled()) { - env.set(DoubleParam.OptimalityTol, config.tolerance()); - env.set(DoubleParam.IntFeasTol, config.tolerance()); + if (config.isToleranceEnabled()) { + env.set(DoubleParam.OptimalityTol, config.getTolerance()); + env.set(DoubleParam.IntFeasTol, config.getTolerance()); } // Timeout? - if (config.timeoutEnabled()) { - env.set(DoubleParam.TimeLimit, config.timeout()); + if (config.isTimeoutEnabled()) { + env.set(DoubleParam.TimeLimit, config.getTimeout()); } // create new Gurobi Model/Problem model = new GRBModel(env); // set output path, if configured - if (config.outputEnabled()) { - this.outputPath = config.outputPath(); + if (config.isOutputEnabled()) { + this.outputPath = config.getOutputPath(); } grbVars.clear(); @@ -91,6 +91,7 @@ private void init() throws GRBException { @Override public void buildILPProblem(Problem problem) { this.problem = problem; + problem.validateConstraints(); // Substitute Or Constraints problem.substituteOr(); @@ -217,7 +218,7 @@ private void translateNormalConstraint(NormalConstraint constraint) { tempLin.addTerm(term.getWeight(), grbVars.get(term.getVar1().getName())); } try { - model.addConstr(tempLin, op, rhs, constraint.toString()); + model.addConstr(tempLin, op, rhs, constraint.getName()); } catch (GRBException e) { throw new RuntimeException(e); } @@ -233,7 +234,7 @@ private void translateNormalConstraint(NormalConstraint constraint) { } } try { - model.addQConstr(tempQuad, op, rhs, constraint.toString()); + model.addQConstr(tempQuad, op, rhs, constraint.getName()); } catch (GRBException e) { throw new RuntimeException(e); } @@ -280,7 +281,7 @@ private void translateGeneralConstraint(GeneralConstraint constraint) { } } model.addGenConstrOr(model.addVar(0.0, 1.0, 0.0, GRB.BINARY, res.getName()), grbVars, - constraint.toString()); + constraint.getName()); } catch (GRBException e) { throw new RuntimeException(e); } @@ -353,13 +354,13 @@ private GRBVar translateIntegerVariable(IntegerVariable variable) { int lb = variable.getLowerBound(); int ub = variable.getUpperBound(); - if (config.boundsEnabled()) { + if (config.isBoundsEnabled()) { if (variable.isDefaultLowerBound()) { - lb = config.lowerBound(); + lb = config.getLowerBound(); variable.setLowerBound((int) lb); } if (variable.isDefaultUpperBound()) { - ub = config.upperBound(); + ub = config.getUpperBound(); variable.setUpperBound((int) ub); } } @@ -386,13 +387,13 @@ private GRBVar translateRealVariable(RealVariable variable) { double lb = variable.getLowerBound(); double ub = variable.getUpperBound(); - if (config.boundsEnabled()) { + if (config.isBoundsEnabled()) { if (variable.isDefaultLowerBound()) { - lb = config.lowerBound(); + lb = config.getLowerBound(); variable.setLowerBound(lb); } if (variable.isDefaultUpperBound()) { - ub = config.upperBound(); + ub = config.getUpperBound(); variable.setUpperBound(ub); } } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/LinearConstraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/LinearConstraint.java index 75a34e0..252a43b 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/LinearConstraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/LinearConstraint.java @@ -11,7 +11,7 @@ * * w_1 * x_1 + w_2 * x_2 + ... (>= | > | = | != | < | <=) rhs */ -public class LinearConstraint implements NormalConstraint { +public class LinearConstraint extends NormalConstraint { private List lhsTerms = new ArrayList(); private Operator op; diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/NormalConstraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/NormalConstraint.java index da349bc..97b03ae 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/NormalConstraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/NormalConstraint.java @@ -2,7 +2,7 @@ import java.util.List; -public interface NormalConstraint extends Constraint { +public abstract class NormalConstraint extends Constraint { /** * Returns a list of the terms on the left-hand side of the constraint. @@ -10,7 +10,7 @@ public interface NormalConstraint extends Constraint { * @return Current list of terms on the left-hand side of the constraint. * @see Term */ - public List getLhsTerms(); + public abstract List getLhsTerms(); /** * Sets the terms of the constraint. @@ -18,7 +18,7 @@ public interface NormalConstraint extends Constraint { * @param lhsTerms New list of terms to be set for the constraint. * @see Term */ - public void setLhsTerms(List lhsTerms); + public abstract void setLhsTerms(final List lhsTerms); /** * Adds a term to the existing terms on the left-hand side of the constraint. @@ -26,7 +26,7 @@ public interface NormalConstraint extends Constraint { * @param term New term to be added to the constraint. * @see Term */ - public void addTerm(Term term); + public abstract void addTerm(final Term term); /** * Returns the operator of the constraint. @@ -34,7 +34,7 @@ public interface NormalConstraint extends Constraint { * @return Current operator of the constraint. * @see Operator */ - public Operator getOp(); + public abstract Operator getOp(); /** * Sets the operator of the constraint. @@ -42,21 +42,21 @@ public interface NormalConstraint extends Constraint { * @param op Operator to be set for the constraint. * @see Operator */ - public void setOp(Operator op); + public abstract void setOp(final Operator op); /** * Returns the value on the right-hand side of the constraint. * * @return Current Value on the right-hand side. */ - public double getRhs(); + public abstract double getRhs(); /** * Sets the value on the right-hand side of the constraint. * * @param rhs New value on the right-hand side. */ - public void setRhs(double rhs); + public abstract void setRhs(final double rhs); /** * Returns the type of the constraint. @@ -64,7 +64,7 @@ public interface NormalConstraint extends Constraint { * @return Type of the constraint. * @see ConstraintType */ - public ConstraintType getType(); + public abstract ConstraintType getType(); /** * Converts the constraint to a list of new constraints, if needed. Only @@ -72,6 +72,6 @@ public interface NormalConstraint extends Constraint { * * @return List of substitution constraints or empty list. */ - public List convertOperator(); + public abstract List convertOperator(); } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/OrConstraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/OrConstraint.java index b734bf3..c85029a 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/OrConstraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/OrConstraint.java @@ -11,7 +11,7 @@ * * @see LinearConstraint */ -public class OrConstraint implements Constraint { +public class OrConstraint extends Constraint { private List constraints; private double epsilon = 1.0E-4; diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/OrVarsConstraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/OrVarsConstraint.java index 36dd651..338ed19 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/OrVarsConstraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/OrVarsConstraint.java @@ -5,7 +5,7 @@ // Currently only used for Gurobi OrConstraints // b_result = or(var1, var2, ..., var_n) -public class OrVarsConstraint implements GeneralConstraint { +public class OrVarsConstraint extends GeneralConstraint { private List variables; private BinaryVariable result; diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/Problem.java b/org.emoflon.ilp/src/org/emoflon/ilp/Problem.java index f718848..73652c3 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/Problem.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/Problem.java @@ -6,6 +6,8 @@ import java.util.Map; import java.util.stream.Collectors; +import org.junit.AssumptionViolatedException; + /** * This class represents the problem to be solved. * @@ -14,13 +16,15 @@ public class Problem { private Function objective; private ObjectiveType type = ObjectiveType.MIN; - private List constraints = new ArrayList(); - private List genConstraints = new ArrayList(); - private List sosConstraints = new ArrayList(); - private List orConstraints = new ArrayList(); + private Map constraints = new HashMap(); + private Map genConstraints = new HashMap(); + private Map sosConstraints = new HashMap(); + private Map orConstraints = new HashMap(); private Map> variables = new HashMap>(); + private int constraintNameGenCounter = 0; + /** * The constructor for a problem. */ @@ -105,7 +109,7 @@ public void setOrConstraints(List constraints) { * @see QuadraticConstraint */ public List getConstraints() { - return constraints; + return new ArrayList(this.constraints.values()); } /** @@ -114,7 +118,7 @@ public List getConstraints() { * @return List of all general constraints. */ public List getGeneralConstraints() { - return genConstraints; + return new ArrayList(this.genConstraints.values()); } /** @@ -124,7 +128,7 @@ public List getGeneralConstraints() { * @see SOS1Constraint */ public List getSOSConstraints() { - return sosConstraints; + return new ArrayList(this.sosConstraints.values()); } /** @@ -134,7 +138,7 @@ public List getSOSConstraints() { * @see OrConstraint */ public List getOrConstraints() { - return orConstraints; + return new ArrayList(this.orConstraints.values()); } /** @@ -196,7 +200,7 @@ public void setObjective(Function objective, ObjectiveType type) { * Sets the objective function of this problem to an empty linear function. */ public void setObjective() { - if (objective.getTerms().isEmpty()) { + if (objective != null && objective.getTerms().isEmpty()) { System.out.println("WARNING: The objective function of this problem is empty."); } this.objective = new LinearFunction(); @@ -257,6 +261,19 @@ public int getVariableCount() { return variables.size(); } + /** + * If the given constraint does not have a name, yet, this method sets a generic + * (unique) name. + * + * @param constraint Constraint to set a generic (unique) name for. + */ + private void genConstraintNameIfAbsent(final Constraint constraint) { + // If constraint has no name, generate one + if (constraint.getName() == null || constraint.getName().isBlank()) { + constraint.setName("cnstr_" + constraintNameGenCounter++); + } + } + /** * Adds a normal constraint to the current constraints. * @@ -265,16 +282,14 @@ public int getVariableCount() { * @see QuadraticConstraint */ public void add(NormalConstraint constraint) { - if (constraint.getLhsTerms().isEmpty()) { - throw new IllegalArgumentException("The left-hand side of a Constraint must not be empty!"); - } + genConstraintNameIfAbsent(constraint); for (Term term : constraint.getLhsTerms()) { variables.put(term.getVar1().getName(), term.getVar1()); if (term instanceof QuadraticTerm) { variables.put(((QuadraticTerm) term).getVar2().getName(), ((QuadraticTerm) term).getVar2()); } } - constraints.add(constraint); + constraints.put(constraint.getName(), constraint); } /** @@ -283,14 +298,12 @@ public void add(NormalConstraint constraint) { * @param constraint General constraint to be added to this problem. */ public void add(GeneralConstraint constraint) { - if (constraint.getVariables().isEmpty()) { - throw new IllegalArgumentException("The variables of a General Constraint must not be empty!"); - } + genConstraintNameIfAbsent(constraint); for (Variable var : constraint.getVariables()) { variables.put(var.getName(), var); } variables.put(constraint.getResult().getName(), constraint.getResult()); - genConstraints.add(constraint); + genConstraints.put(constraint.getName(), constraint); } /** @@ -300,15 +313,11 @@ public void add(GeneralConstraint constraint) { * @see SOS1Constraint */ public void add(SOS1Constraint constraint) { - if (constraint.getVariables().isEmpty()) { - throw new IllegalArgumentException("The variables of a SOS Constraint must not be empty!"); - } else if (constraint.getVariables().size() != constraint.getWeights().length) { - throw new IllegalArgumentException("Every variable in a SOS Constraint should be assigned with a weight!"); - } + genConstraintNameIfAbsent(constraint); for (Variable var : constraint.getVariables()) { variables.put(var.getName(), var); } - sosConstraints.add(constraint); + sosConstraints.put(constraint.getName(), constraint); } /** @@ -318,15 +327,13 @@ public void add(SOS1Constraint constraint) { * @see OrConstraint */ public void add(OrConstraint constraint) { - if (constraint.getConstraints().isEmpty()) { - throw new IllegalArgumentException("The constraints of a Or Constraint must not be empty!"); - } + genConstraintNameIfAbsent(constraint); for (LinearConstraint lin : constraint.getConstraints()) { for (Term term : lin.getLhsTerms()) { variables.put(term.getVar1().getName(), term.getVar1()); } } - orConstraints.add(constraint); + orConstraints.put(constraint.getName(), constraint); } /** @@ -338,7 +345,7 @@ public void add(OrConstraint constraint) { * problem formulation. */ public boolean remove(NormalConstraint constraint) { - return constraints.remove(constraint); + return constraints.remove(constraint.getName()) != null; } /** @@ -350,7 +357,7 @@ public boolean remove(NormalConstraint constraint) { * problem formulation. */ public boolean remove(GeneralConstraint constraint) { - return genConstraints.remove(constraint); + return genConstraints.remove(constraint.getName()) != null; } /** @@ -362,7 +369,7 @@ public boolean remove(GeneralConstraint constraint) { * problem formulation. */ public boolean remove(SOS1Constraint constraint) { - return sosConstraints.remove(constraint); + return sosConstraints.remove(constraint.getName()) != null; } /** @@ -374,7 +381,7 @@ public boolean remove(SOS1Constraint constraint) { * problem formulation. */ public boolean remove(OrConstraint constraint) { - return orConstraints.remove(constraint); + return orConstraints.remove(constraint.getName()) != null; } /** @@ -437,7 +444,9 @@ public void substituteOperators() { .map(SOS1Constraint.class::cast).collect(Collectors.toList())); } // delete converted constraints - this.constraints.removeAll(delete); + for (final NormalConstraint toDelete : delete) { + this.constraints.remove(toDelete.getName()); + } delete.forEach(it -> this.remove(it)); // add substitutions @@ -467,4 +476,69 @@ public void substituteSOS1() { sosConstraints.clear(); } -} \ No newline at end of file + /** + * Returns the constraint for the given name or throws an error if there is no + * constraint with a matching name. + * + * @param name Constraint name to search for. + * @return Constraint for the given name. + */ + public Constraint getConstraintByName(final String name) { + if (this.constraints.containsKey(name)) { + return this.constraints.get(name); + } else if (this.genConstraints.containsKey(name)) { + return this.genConstraints.get(name); + } else if (this.sosConstraints.containsKey(name)) { + return this.sosConstraints.get(name); + } else if (this.orConstraints.containsKey(name)) { + return this.orConstraints.get(name); + } else { + throw new Error("Constraint with name " + name + " not found."); + } + } + + /** + * Returns true if the problem contains a constraint with the given name. + * + * @param name Name to check the constraint existence for. + * @return True if the problem contains a constraint with the given name. + */ + public boolean hasConstraintWithName(final String name) { + return this.constraints.containsKey(name) || this.genConstraints.containsKey(name) + || this.sosConstraints.containsKey(name) || this.orConstraints.containsKey(name); + } + + /** + * Validates all constraints that are part of this problem instance. + */ + public void validateConstraints() { + this.constraints.values().forEach(v -> { + if (v.getLhsTerms().isEmpty()) { + throw new AssumptionViolatedException("The constraint " + v.getName() + " has no LHS terms."); + } + }); + + this.genConstraints.values().forEach(v -> { + if (v.getVariables().isEmpty()) { + throw new AssumptionViolatedException("The generic constraint " + v.getName() + " has no variables."); + } + }); + + this.sosConstraints.values().forEach(v -> { + if (v.getVariables().isEmpty()) { + throw new AssumptionViolatedException("The SOS1 constraint " + v.getName() + " has no variables."); + } + if (v.getVariables().size() != v.getWeights().length) { + throw new AssumptionViolatedException("The SOS1 constraint " + v.getName() + + " does not contain the correct amount of weights for the number of variables."); + } + }); + + this.orConstraints.values().forEach(v -> { + if (v.getConstraints().isEmpty()) { + throw new AssumptionViolatedException("The Or constraint " + v.getName() + " has no sub constraints."); + } + }); + } + +} diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/QuadraticConstraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/QuadraticConstraint.java index 7a36206..383e8a4 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/QuadraticConstraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/QuadraticConstraint.java @@ -13,7 +13,7 @@ * * w_1 * x_1 + w_2 * x_2 + ... (>= | > | = | != | < | <=) rhs */ -public class QuadraticConstraint implements NormalConstraint { +public class QuadraticConstraint extends NormalConstraint { private List lhsTerms; private Operator op; diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/SOS1Constraint.java b/org.emoflon.ilp/src/org/emoflon/ilp/SOS1Constraint.java index 13aea63..7e02f3e 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/SOS1Constraint.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/SOS1Constraint.java @@ -12,7 +12,7 @@ * SOS1(var_1, var_2, ..., var_n) with weights w = [w_1, w_2, ..., w_n] * */ -public class SOS1Constraint implements Constraint { +public class SOS1Constraint extends Constraint { private List> variables = new ArrayList>(); private List weights = new ArrayList(); diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/SolverConfig.java b/org.emoflon.ilp/src/org/emoflon/ilp/SolverConfig.java index a91f2f7..9dc3e44 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/SolverConfig.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/SolverConfig.java @@ -1,46 +1,208 @@ package org.emoflon.ilp; -import org.emoflon.ilp.SolverConfig.SolverType; - /** - * This record class represents the configuration parameters used to configure - * the solver. - * - * @param type Solver Type (GUROBI, GLPK, CPLEX) - * @param timeoutEnabled Set to true, if timeout should be set. - * @param timeout time until timeout - * @param randomSeedEnabled Set to true, if a seed should be set (if possible). - * @param randomSeed Value for the random seed. - * @param toleranceEnabled Set to true, if a value for the tolerance should be - * set. - * @param tolerance Value for the tolerance. - * @param boundsEnabled Set to true, if custom default bounds for integer - * and real variables should be set. - * @param lowerBound Value for the custom upper bound for integer and - * real variables. - * @param upperBound Value for the custom upper bound for integer and - * real variables. - * @param presolveEnabled Set to true, if the solver should carry out a - * presolve phase (for GPLK you might need to set this - * to true!). - * @param debugOutputEnabled Set to true, if debug output should be written into - * a file. - * @param outputEnabled Set to true, if the problem should be written into - * a file (e.g. with the file ending ".lp") - * @param outputPath Path to the output file. - * + * This class represents the configuration parameters used to configure the + * solver. */ -public record SolverConfig(SolverType solver, boolean timeoutEnabled, double timeout, boolean randomSeedEnabled, - int randomSeed, boolean toleranceEnabled, double tolerance, boolean boundsEnabled, int lowerBound, - int upperBound, boolean presolveEnabled, boolean debugOutputEnabled, boolean outputEnabled, String outputPath) { +public class SolverConfig { + private SolverType solver; + private boolean timeoutEnabled; + private double timeout; + private boolean randomSeedEnabled; + private int randomSeed; + private boolean toleranceEnabled; + private double tolerance; + private boolean boundsEnabled; + private int lowerBound; + private int upperBound; + private boolean presolveEnabled; + private boolean debugOutputEnabled; + private boolean outputEnabled; + private String outputPath; + + /** + * Creates a new instance of the solver configuration. + * + * @param type Solver Type (GUROBI, GLPK, CPLEX) + * @param timeoutEnabled Set to true, if timeout should be set. + * @param timeout time until timeout + * @param randomSeedEnabled Set to true, if a seed should be set (if possible). + * @param randomSeed Value for the random seed. + * @param toleranceEnabled Set to true, if a value for the tolerance should be + * set. + * @param tolerance Value for the tolerance. + * @param boundsEnabled Set to true, if custom default bounds for integer + * and real variables should be set. + * @param lowerBound Value for the custom upper bound for integer and + * real variables. + * @param upperBound Value for the custom upper bound for integer and + * real variables. + * @param presolveEnabled Set to true, if the solver should carry out a + * presolve phase (for GPLK you might need to set this + * to true!). + * @param debugOutputEnabled Set to true, if debug output should be written into + * a file. + * @param outputEnabled Set to true, if the problem should be written into + * a file (e.g. with the file ending ".lp") + * @param outputPath Path to the output file. + */ + public SolverConfig(SolverType solver, boolean timeoutEnabled, double timeout, boolean randomSeedEnabled, + int randomSeed, boolean toleranceEnabled, double tolerance, boolean boundsEnabled, int lowerBound, + int upperBound, boolean presolveEnabled, boolean debugOutputEnabled, boolean outputEnabled, + String outputPath) { + this.solver = solver; + + this.timeoutEnabled = timeoutEnabled; + this.timeout = timeout; + this.randomSeedEnabled = randomSeedEnabled; + this.randomSeed = randomSeed; + this.toleranceEnabled = toleranceEnabled; + this.tolerance = tolerance; + this.boundsEnabled = boundsEnabled; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + this.presolveEnabled = presolveEnabled; + this.debugOutputEnabled = debugOutputEnabled; + this.outputEnabled = outputEnabled; + this.outputPath = outputPath; + } + + public SolverConfig() { + this.solver = SolverType.GLPK; + this.timeoutEnabled = false; + this.timeout = -1; + this.randomSeedEnabled = false; + this.randomSeed = -1; + this.toleranceEnabled = false; + this.tolerance = -1; + this.boundsEnabled = false; + this.lowerBound = -1; + this.upperBound = -1; + this.presolveEnabled = true; + this.debugOutputEnabled = true; + this.outputEnabled = false; + this.outputPath = "/dev/null"; + } /** * Type of the Solver
- *
* - * GUROBI, GLPK or CPLEX + * GUROBI, GLPK, or CPLEX */ public enum SolverType { GUROBI, CPLEX, GLPK } + + public SolverType getSolver() { + return solver; + } + + public void setSolver(final SolverType solver) { + this.solver = solver; + } + + public boolean isTimeoutEnabled() { + return timeoutEnabled; + } + + public void setTimeoutEnabled(final boolean timeoutEnabled) { + this.timeoutEnabled = timeoutEnabled; + } + + public double getTimeout() { + return timeout; + } + + public void setTimeout(final double timeout) { + this.timeout = timeout; + } + + public boolean isRandomSeedEnabled() { + return randomSeedEnabled; + } + + public void setRandomSeedEnabled(final boolean randomSeedEnabled) { + this.randomSeedEnabled = randomSeedEnabled; + } + + public int getRandomSeed() { + return randomSeed; + } + + public void setRandomSeed(final int randomSeed) { + this.randomSeed = randomSeed; + } + + public boolean isToleranceEnabled() { + return toleranceEnabled; + } + + public void setToleranceEnabled(final boolean toleranceEnabled) { + this.toleranceEnabled = toleranceEnabled; + } + + public double getTolerance() { + return tolerance; + } + + public void setTolerance(final double tolerance) { + this.tolerance = tolerance; + } + + public boolean isBoundsEnabled() { + return boundsEnabled; + } + + public void setBoundsEnabled(final boolean boundsEnabled) { + this.boundsEnabled = boundsEnabled; + } + + public int getLowerBound() { + return lowerBound; + } + + public void setLowerBound(final int lowerBound) { + this.lowerBound = lowerBound; + } + + public int getUpperBound() { + return upperBound; + } + + public void setUpperBound(final int upperBound) { + this.upperBound = upperBound; + } + + public boolean isPresolveEnabled() { + return presolveEnabled; + } + + public void setPresolveEnabled(final boolean presolveEnabled) { + this.presolveEnabled = presolveEnabled; + } + + public boolean isDebugOutputEnabled() { + return debugOutputEnabled; + } + + public void setDebugOutputEnabled(final boolean debugOutputEnabled) { + this.debugOutputEnabled = debugOutputEnabled; + } + + public boolean isOutputEnabled() { + return outputEnabled; + } + + public void setOutputEnabled(final boolean outputEnabled) { + this.outputEnabled = outputEnabled; + } + + public String getOutputPath() { + return outputPath; + } + + public void setOutputPath(final String outputPath) { + this.outputPath = outputPath; + } + } diff --git a/org.emoflon.ilp/src/org/emoflon/ilp/SolverHelper.java b/org.emoflon.ilp/src/org/emoflon/ilp/SolverHelper.java index 3230345..b4f9c3c 100644 --- a/org.emoflon.ilp/src/org/emoflon/ilp/SolverHelper.java +++ b/org.emoflon.ilp/src/org/emoflon/ilp/SolverHelper.java @@ -18,7 +18,7 @@ public class SolverHelper { * @see SolverConfig */ public SolverHelper(SolverConfig config) { - switch (config.solver()) { + switch (config.getSolver()) { case GUROBI: this.solver = new GurobiSolver(config); break;