diff --git a/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java b/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java index 180b8aa4fc704d7f710dbf8e89ede1d795cc6a2a..64dbd55f492f6349ea5a41a3783129576b1fbe34 100644 --- a/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java +++ b/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java @@ -1,9 +1,10 @@ package de.tudresden.inf.st.mquat.benchmark; import de.tudresden.inf.st.mquat.solving.BenchmarkableSolver; +import de.tudresden.inf.st.mquat.solving.ilp.GLPKSolver; import de.tudresden.inf.st.mquat.solving.ilp.ILPDirectSolver; -import de.tudresden.inf.st.mquat.solving.ilp.ILPExternalSolver; import de.tudresden.inf.st.mquat.solving.genetic.GeneticSolver; +import de.tudresden.inf.st.mquat.solving.ilp.SCIPSolver; import de.tudresden.inf.st.mquat.solving.random.RandomSolver; import de.tudresden.inf.st.mquat.solving.simple.SimpleSolver; import ir.ac.ui.eng.ACOSolver; @@ -28,7 +29,8 @@ public class SolverFactory { availableSolvers = Stream.of( new ACOSolver(), new EMFeRSolver(), - new ILPExternalSolver(), + new GLPKSolver(), + new SCIPSolver(), new ILPDirectSolver(), new SimpleSolver(), new RandomSolver(0, 0), diff --git a/jastadd-mquat-benchmark/src/main/resources/scenarios.json b/jastadd-mquat-benchmark/src/main/resources/scenarios.json index e9163b0d186054d7c07d36da5786ab42521065c5..54b41792909b98907ea47a5293eda672b172c6c0 100644 --- a/jastadd-mquat-benchmark/src/main/resources/scenarios.json +++ b/jastadd-mquat-benchmark/src/main/resources/scenarios.json @@ -5,7 +5,8 @@ "aco", "emfer", "ilp-direct", -// "ilp-external", + "ilp-glpk", + "ilp-scip", "random", "genetic", "simple" diff --git a/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/GLPKSolver.java b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/GLPKSolver.java new file mode 100644 index 0000000000000000000000000000000000000000..254bd331c408fd0239930154c2bc2c936b09ecb9 --- /dev/null +++ b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/GLPKSolver.java @@ -0,0 +1,104 @@ +package de.tudresden.inf.st.mquat.solving.ilp; + +import de.tudresden.inf.st.mquat.jastadd.model.ILP; +import de.tudresden.inf.st.mquat.jastadd.model.IlpVariable; +import de.tudresden.inf.st.mquat.solving.SolvingException; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class GLPKSolver extends ILPExternalSolver { + + /** + * Create a new GLPK solver with default settings. + * Default is: + * <ul> + * <li>1 minute timeout</li> + * <li>delete temporary files on exit.</li> + * </ul> + * @see GLPKSolver#setDeleteFilesOnExit(boolean) + */ + public GLPKSolver() { + } + + @Override + protected String[] getCommand(Path lp, Path solution, long remainingTimeForSolvingInMillis) { + return new String[] {"glpsol", "--lp", lp.toAbsolutePath().toString(), "--cuts", + "-o", String.valueOf(solution.toAbsolutePath())}; + } + + @Override + public String getName() { + return "ilp-glpk"; + } + + @Override + protected void readFromPlainTextSolution(ILP ilp, Path solution, ILPSolution result, + List<IlpVariable> variablesSetToOne) throws SolvingException { + List<String> varNamesSetToOne = new ArrayList<>(); + String name = null; + int phase = 1; + try (Stream<String> lines = Files.lines(solution)) { + for (String line : lines.collect(Collectors.toList())) { + if (phase < 3) { + if (line.startsWith("Objective")) { + int equalsIndex = line.indexOf('='); + int bracketIndex = line.lastIndexOf('('); + result.setObjective(Double.valueOf(line.substring(equalsIndex + 1, bracketIndex).trim())); + } + if (line.startsWith("---")) { + phase += 1; + } + continue; + } + line = line.trim(); + if (line.isEmpty()) { + continue; + } + String[] tokens = line.split("\\s+"); + if (tokens.length == 6) { + // tokens: index, name, star, activity, lb, rb + if(Integer.valueOf(tokens[3]) == 1) { + varNamesSetToOne.add(tokens[1]); + } + phase = 3; + } else if (phase == 3) { + if(line.startsWith("Integer")) { + break; + } + // tokens: index, name + name = tokens[1]; + phase = 4; + } else if (phase == 4) { + // tokens: star, activity, lb, rb + if (name == null) { + throw new SolvingException("Error in parsing solution. Name is null. Tokens: " + Arrays.toString(tokens)); + } + if (Integer.valueOf(tokens[1]) == 1) { + varNamesSetToOne.add(name); + name = null; + } + phase = 3; + } + } + } catch (IOException e) { + throw new SolvingException("Could not open solution file", e); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + throw new SolvingException("Could not parse solution file", e); + } + for (String varName : varNamesSetToOne) { + IlpVariable variable = ilp.resolve(varName); + if (variable == null) { + throw new SolvingException("Could not find variable with name " + varName); + } + variablesSetToOne.add(variable); + } + } + +} diff --git a/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPExternalSolver.java b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPExternalSolver.java index f6f8ab36a35dad7cb5235b994015f528b22535e9..265e64017a3efa55c514fa284d7549fadc696f56 100644 --- a/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPExternalSolver.java +++ b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPExternalSolver.java @@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; -public class ILPExternalSolver extends AbstractILPSolver { +public abstract class ILPExternalSolver extends AbstractILPSolver { private boolean deleteFilesOnExit; private Path lp, solutionReadable; @@ -71,12 +71,9 @@ public class ILPExternalSolver extends AbstractILPSolver { } protected double solve0(Root model, StopWatch watch, List<IlpVariable> variablesSetToOne) throws SolvingException { - - long startOfWriteOutInMillis = watch.time(TimeUnit.MILLISECONDS); - // Create temporary files try { - lp = Files.createTempFile("ilp", null); + lp = Files.createTempFile("ilp", ".lp"); // solution = Files.createTempFile("solution", null); solutionReadable = Files.createTempFile("sol-read", null); } catch (IOException e) { throw new SolvingException("Can not create lp or solution file", e); } @@ -84,8 +81,12 @@ public class ILPExternalSolver extends AbstractILPSolver { logger.info("Writing ILP to {}, solving now", lp.toAbsolutePath()); } + + long startOfWriteOutInMillis = watch.time(TimeUnit.MILLISECONDS); + // write out lp file IlpString output = model.getILP().printIlp(); + try (BufferedWriter writer = Files.newBufferedWriter( lp, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { writer.write(output.toString()); @@ -95,22 +96,18 @@ public class ILPExternalSolver extends AbstractILPSolver { this.lastGeneration = watch.time(TimeUnit.MILLISECONDS); long millisecondsNeededToWriteOut = watch.time(TimeUnit.MILLISECONDS) - startOfWriteOutInMillis; long remainingTimeInMillis = this.timeoutUnit.toMillis(this.timeoutValue) - lastGeneration; - long remainingTimeForSolvingInMillis = Math.max(0, remainingTimeInMillis - 2*millisecondsNeededToWriteOut); + long remainingTimeForSolvingInMillis = Math.max(0, remainingTimeInMillis - millisecondsNeededToWriteOut); // take twice the time to have buffer to write out solution afterwards // start GLPK to solve the lp file just written, writing out the solution Process process; - String command = "glpsol --lp " + lp.toAbsolutePath() + -// " -w " + solution.toAbsolutePath() + - " --tmlim " + remainingTimeForSolvingInMillis/1000 + - " --cuts" + - " -o " + solutionReadable.toAbsolutePath(); - logger.debug("Call: '{}'", command); + String[] command = getCommand(lp, solutionReadable, remainingTimeForSolvingInMillis); + logger.debug("Call: '{}'", String.join(" ", command)); try { process = Runtime.getRuntime().exec(command,null, new File(".")); } catch (IOException e) { - throw new SolvingException("Problem calling glpsol. Is it installed?", e); + throw new SolvingException("Problem calling solver. Is it installed?", e); } boolean finishedInTime; try { @@ -149,71 +146,9 @@ public class ILPExternalSolver extends AbstractILPSolver { return result.getObjective(); } - private static void readFromPlainTextSolution(ILP ilp, Path solution, ILPSolution result, - List<IlpVariable> variablesSetToOne) throws SolvingException { - List<String> varNamesSetToOne = new ArrayList<>(); - String name = null; - int phase = 1; - try (Stream<String> lines = Files.lines(solution)) { - for (String line : lines.collect(Collectors.toList())) { - if (phase < 3) { - if (line.startsWith("Objective")) { - int equalsIndex = line.indexOf('='); - int bracketIndex = line.lastIndexOf('('); - result.setObjective(Double.valueOf(line.substring(equalsIndex + 1, bracketIndex).trim())); - } - if (line.startsWith("---")) { - phase += 1; - } - continue; - } - line = line.trim(); - if (line.isEmpty()) { - continue; - } - String[] tokens = line.split("\\s+"); - if (tokens.length == 6) { - // tokens: index, name, star, activity, lb, rb - if(Integer.valueOf(tokens[3]) == 1) { - varNamesSetToOne.add(tokens[1]); - } - phase = 3; - } else if (phase == 3) { - if(line.startsWith("Integer")) { - break; - } - // tokens: index, name - name = tokens[1]; - phase = 4; - } else if (phase == 4) { - // tokens: star, activity, lb, rb - if (name == null) { - throw new SolvingException("Error in parsing solution. Name is null. Tokens: " + Arrays.toString(tokens)); - } - if (Integer.valueOf(tokens[1]) == 1) { - varNamesSetToOne.add(name); - name = null; - } - phase = 3; - } - } - } catch (IOException e) { - throw new SolvingException("Could not open solution file", e); - } catch (NumberFormatException | IndexOutOfBoundsException e) { - throw new SolvingException("Could not parse solution file", e); - } - for (String varName : varNamesSetToOne) { - IlpVariable variable = ilp.resolve(varName); - if (variable == null) { - throw new SolvingException("Could not find variable with name " + varName); - } - variablesSetToOne.add(variable); - } - } + protected abstract void readFromPlainTextSolution(ILP ilp, Path solution, ILPSolution result, + List<IlpVariable> variablesSetToOne) throws SolvingException; - @Override - public String getName() { - return "ilp-external"; - } + protected abstract String[] getCommand(Path lp, Path solution, long remainingTimeForSolvingInMillis); } diff --git a/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPMain.java b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPMain.java index bc8b5f2761be6d9367e16530dd52af52bacba405..3538ce52d36b140ab926dd76e7258b5e32872da5 100644 --- a/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPMain.java +++ b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/ILPMain.java @@ -74,7 +74,7 @@ public class ILPMain { } private static void solveILPWithSolver(Root model) throws SolvingException { - ILPExternalSolver solver = new ILPExternalSolver(); + ILPExternalSolver solver = new GLPKSolver().setDeleteFilesOnExit(false); Solution solution = solver.solve(model); System.out.println(solution); } @@ -90,7 +90,7 @@ public class ILPMain { System.out.println(version); ScenarioGenerator gen = new ScenarioGenerator(new ScenarioDescription(1, 2, 0, 0, 0, 2, 2, 2.5, 3, 1, 0)); Root model = gen.generate(); - Solver external = new ILPExternalSolver().setDeleteFilesOnExit(false); + Solver external = new GLPKSolver().setDeleteFilesOnExit(false); Solution solution = external.solve(model); logger.info(model.print(new MquatWriteSettings(" "))); solution.explain(); diff --git a/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/SCIPSolver.java b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/SCIPSolver.java new file mode 100644 index 0000000000000000000000000000000000000000..3c30913db3319dd25b9c9a02cd0e442979042c90 --- /dev/null +++ b/jastadd-mquat-solver-ilp/src/main/java/de/tudresden/inf/st/mquat/solving/ilp/SCIPSolver.java @@ -0,0 +1,79 @@ +package de.tudresden.inf.st.mquat.solving.ilp; + +import de.tudresden.inf.st.mquat.jastadd.model.ILP; +import de.tudresden.inf.st.mquat.jastadd.model.IlpVariable; +import de.tudresden.inf.st.mquat.solving.SolvingException; +import org.apache.logging.log4j.LogManager; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SCIPSolver extends ILPExternalSolver { + + /** + * Create a new GLPK solver with default settings. + * Default is: + * <ul> + * <li>1 minute timeout</li> + * <li>delete temporary files on exit.</li> + * </ul> + * @see SCIPSolver#setDeleteFilesOnExit(boolean) + */ + public SCIPSolver() { + } + + + @Override + protected String[] getCommand(Path lp, Path solution, long remainingTimeForSolvingInMillis) { + String[] command = {"scip", "-c", "read " + lp.toAbsolutePath() + " set limit time " + remainingTimeForSolvingInMillis/1000 + " optimize write solution " + solution.toAbsolutePath() + " quit"}; + return command; + } + + @Override + protected void readFromPlainTextSolution(ILP ilp, Path solution, ILPSolution result, + List<IlpVariable> variablesSetToOne) throws SolvingException { + try (Stream<String> lines = Files.lines(solution)) { + for (String line : lines.collect(Collectors.toList())) { + if (line.startsWith("objective value:")) { + result.setObjective(Double.valueOf(line.substring(16).trim())); + logger.debug("read objective {}", Double.valueOf(line.substring(16).trim())); + } else if (line.startsWith("solution status:")) { + logger.debug("we got a solution status {}", line.substring(16).trim()); + } else if (line.startsWith("no solution available")) { + logger.debug("no solution was found!"); + return; + } else { + String[] tokens = line.split("\\s+"); + if (tokens.length == 3) { + // tokens: name, value, objective + + if (Math.round(Double.parseDouble(tokens[1])*1000000000)==1000000000) { + logger.debug("found new variable {} with value {}", tokens[0], tokens[1]); + IlpVariable variable = ilp.resolve(tokens[0]); + if (variable == null) { + throw new SolvingException("Could not find variable with name " + tokens[0]); + } + variablesSetToOne.add(variable); + } + } + } + } + } catch (IOException e) { + throw new SolvingException("Could not open solution file", e); + } catch (NumberFormatException e) { + throw new SolvingException("Could not parse solution file", e); + } + } + + @Override + public String getName() { + return "ilp-scip"; + } + +} diff --git a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/GLPKHandwrittenTest.java b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/GLPKHandwrittenTest.java new file mode 100644 index 0000000000000000000000000000000000000000..afc71a9b17a811cfad2b6bc6d8876957783895c6 --- /dev/null +++ b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/GLPKHandwrittenTest.java @@ -0,0 +1,11 @@ +package de.tudresden.inf.st.mquat.solving; + +import de.tudresden.inf.st.mquat.solving.ilp.GLPKSolver; + +public class GLPKHandwrittenTest extends HandwrittenTestSuite { + @Override + protected Solver getSolver() { + // set to false for debugging + return new GLPKSolver().setDeleteFilesOnExit(false); + } +} diff --git a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPExternalHandwrittenTest.java b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPExternalHandwrittenTest.java deleted file mode 100644 index 2abeb4dbdfe7b1f766d040c7826d984b98968b3e..0000000000000000000000000000000000000000 --- a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPExternalHandwrittenTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.tudresden.inf.st.mquat.solving; - -import de.tudresden.inf.st.mquat.solving.ilp.ILPExternalSolver; - -public class ILPExternalHandwrittenTest extends HandwrittenTestSuite { - @Override - protected Solver getSolver() { - // set to false for debugging - return new ILPExternalSolver().setDeleteFilesOnExit(true); - } -} diff --git a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPObjectiveTest.java b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPObjectiveTest.java index 7e5c761bb7d2b829ad0db0cb75864e80bcb48a3e..bd7868edae83d9b0979c86926511ae8df915e63e 100644 --- a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPObjectiveTest.java +++ b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPObjectiveTest.java @@ -4,6 +4,7 @@ import de.tudresden.inf.st.mquat.generator.ScenarioDescription; import de.tudresden.inf.st.mquat.generator.ScenarioGenerator; import de.tudresden.inf.st.mquat.jastadd.model.Root; import de.tudresden.inf.st.mquat.jastadd.model.Solution; +import de.tudresden.inf.st.mquat.solving.ilp.GLPKSolver; import de.tudresden.inf.st.mquat.solving.ilp.ILPExternalSolver; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -36,7 +37,7 @@ public class ILPObjectiveTest { ScenarioGenerator generator = new ScenarioGenerator(new ScenarioDescription(tlc, iac, isd, cac, csd, dep, imp, res, req, cpu, seed)); Root model = generator.generate(); - ILPExternalSolver solver = new ILPExternalSolver().setDeleteFilesOnExit(false); + ILPExternalSolver solver = new GLPKSolver().setDeleteFilesOnExit(false); Solution solution = solver.solve(model); Assert.assertTrue(solution.isValid()); logger.info("Solution (objective={}): {}", solution.computeObjective(), solution); diff --git a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPSolveTest.java b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPSolveTest.java index 63ac7cbd3e402be32cbc1150ede363c0871994fc..a802fa02acb3a01b78c05b9a4eb095134fffaa1a 100644 --- a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPSolveTest.java +++ b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/ILPSolveTest.java @@ -2,6 +2,7 @@ package de.tudresden.inf.st.mquat.solving; import de.tudresden.inf.st.mquat.data.TestGeneratorSettings; import de.tudresden.inf.st.mquat.generator.ScenarioGenerator; +import de.tudresden.inf.st.mquat.solving.ilp.GLPKSolver; import de.tudresden.inf.st.mquat.utils.TestUtils; import de.tudresden.inf.st.mquat.jastadd.model.Root; import de.tudresden.inf.st.mquat.jastadd.model.Solution; @@ -106,7 +107,7 @@ public class ILPSolveTest { private Solver externalSolver() { // set to false to analyse created temporary files - return new ILPExternalSolver().setDeleteFilesOnExit(true).setTimeout(10, TimeUnit.SECONDS); + return new GLPKSolver().setDeleteFilesOnExit(true).setTimeout(10, TimeUnit.SECONDS); } private Solver directSolver() { diff --git a/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/SCIPHandwrittenTest.java b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/SCIPHandwrittenTest.java new file mode 100644 index 0000000000000000000000000000000000000000..791e02ce345304e4025a68e14495e5dfef127d86 --- /dev/null +++ b/jastadd-mquat-solver-ilp/src/test/java/de/tudresden/inf/st/mquat/solving/SCIPHandwrittenTest.java @@ -0,0 +1,11 @@ +package de.tudresden.inf.st.mquat.solving; + +import de.tudresden.inf.st.mquat.solving.ilp.SCIPSolver; + +public class SCIPHandwrittenTest extends HandwrittenTestSuite { + @Override + protected Solver getSolver() { + // set to false for debugging + return new SCIPSolver().setDeleteFilesOnExit(false); + } +}