/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package csp.backends;
import csp.datatypes.CSPSolution;
import csp.datatypes.minion.*;
import finiteReduction.DomainFinder;
import finiteReduction.FiniteReductionConfig;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
/**
*
* @author Jeroen Janssen <Jeroen.Janssen@vub.ac.be>
*/
public class MinionSolver {
private String minionPath;
private File temp;
// We need to keep track of the outputted variables as minion only outputs
// Sol: value in the order in which the variables were created, without
// naming the variable in its output.
private ArrayList<MinionDomainVariable> dVars;
private ArrayList<MinionEnumeratedVariable> eVars;
private int timeout = 1000;
private boolean foundSolution = false;
private static final String NL = System.getProperty("line.separator");
public MinionSolver(String minionPath) {
this.minionPath = minionPath;
}
// TODO: lots of the stuff in here should actually move to the IntegerToMinionConvertor
// as it is MinionSpecific. For example the translation of LEQ and GEQ to sum expressions
// should be in IntegerToMinionConvertor
public void read(MinionCSPProblem model) {
// Sets up the environment for solving
try {
temp = File.createTempFile("furet", "minion");
System.err.println("Temp File created at: " + temp.getAbsolutePath());
// TODO DEBUG
//temp.deleteOnExit();
BufferedWriter out = new BufferedWriter(new FileWriter(temp));
//out.write("aString");
out.write("MINION 3" + NL);
out.write("**VARIABLES**" + NL);
dVars = new ArrayList<MinionDomainVariable>();
for (MinionDomainVariable dVar : model.getDomainVariables()) {
dVars.add(dVar);
out.write("BOUND " + dVar.getName() + " {" + dVar.getLowerBound()
+ ".." + dVar.getUpperBound() + "}" + NL);
}
//HashSet<MinionEnumeratedVariable> eVars = model.getEnumeratedVariables();
eVars = new ArrayList<MinionEnumeratedVariable>();
for (MinionEnumeratedVariable eVar : model.getEnumeratedVariables()) {
eVars.add(eVar);
out.write("SPARSEBOUND " + eVar.getName() + " {");
Iterator<Integer> it = eVar.getValues().iterator();
if (it.hasNext()) {
out.write(it.next().toString());
}
while (it.hasNext()) {
out.write("," + it.next());
}
out.write("}" + NL);
}
out.write("**CONSTRAINTS**" + NL);
for (MinionConstraint c : model.getConstraints()) {
MinionConstraint.Operator op = c.getOperator();
//System.out.println("Operator: " + op);
MinionOutputter mOutput = new MinionOutputter();
String lhs = c.getLHS().accept(mOutput);
String rhs = c.getRHS().accept(mOutput);
if (op.equals(MinionConstraint.Operator.GEQ)) {
// TODO HACK
//sumleq is correct: we have to reverse the operands
out.write("sumleq(");
out.write("[" + rhs + "]");
out.write(",");
out.write(lhs);
out.write(")");
out.write(NL);
} else if (op.equals(MinionConstraint.Operator.LEQ)) {
out.write("sumgeq(");
out.write("[" + rhs + "]");
out.write(",");
out.write(lhs);
out.write(")");
out.write(NL);
} else if (op.equals(MinionConstraint.Operator.EQ)) {
// TODO: remove redundancy
out.write("sumleq(");
out.write("[" + rhs + "]");
out.write(",");
out.write(lhs);
out.write(")");
out.write(NL);
out.write("sumgeq(");
out.write("[" + rhs + "]");
out.write(",");
out.write(lhs);
out.write(")");
out.write(NL);
} else if (op.equals(MinionConstraint.Operator.MAX)) {
out.write("max(" + rhs + "," + lhs + ")");
out.write(NL);
} else if (op.equals(MinionConstraint.Operator.SUM)) {
out.write("sumleq(" + rhs + "," + lhs + ")");
out.write(NL);
out.write("sumgeq(" + rhs + "," + lhs + ")");
out.write(NL);
} else if (op.equals(MinionConstraint.Operator.MULT)) {
// This is a special case as MINION does not support
// product([x,y],z) which would be much more coherent
// with max and plus. We need to output product(x,y,z)
// instead,so we need to get the elements out of our
// expressionlist on the right hand side (provided our
// minion instance is generated by the generator).
// TODO: HACK
// Our lhs should be the dummy variable, so only rhs
// needs special processing and this should be a list of
// two elements *fingers crossed*
MinionExpressionList rhsExp = (MinionExpressionList) c.getRHS();
Iterator<MinionExpression> it = rhsExp.getList().iterator();
String rhs1 = it.next().accept(mOutput);
String rhs2 = it.next().accept(mOutput);
out.write("product(" + rhs1 + "," + rhs2 + "," + lhs + ")");
out.write(NL);
} else {
System.err.println("Unknown operator: " + op);
}
}
out.write("**EOF**" + NL);
out.close();
} catch (IOException e) {
System.err.println("Could not create temp file for solving: " + e);
}
}
private CSPSolution parseMinionOutput(BufferedReader reader) {
CSPSolution model = new CSPSolution();
int i = 0;
int j = 0;
String line;
String solutionPrefix = "Sol: ";
try {
while ((line = reader.readLine()) != null) {
//System.out.println(line);
if (line.startsWith(solutionPrefix)) {
Integer value = Integer.parseInt(line.trim().substring(solutionPrefix.length()));
String variable = "";
if (i < dVars.size()) {
variable = dVars.get(i++).getName();
} else if (j < eVars.size()) {
variable = eVars.get(j++).getName();
}
// TODO: remove hardcoded dummy string!!
// and remove duplication of this string in the convertor
// and solver somehow ...
// We make sure that dummy variables are not output.
if (!variable.startsWith("miniondummy"))
model.addVariable(variable, value);
}
}
if (i == 0 && j == 0) {
return null;
} else {
return model;
}
} catch (Exception err) {
System.err.println("Error parsing minion output.");
err.printStackTrace();
return null;
}
}
public CSPSolution solve() {
CSPSolution model = null;
try {
Process p = Runtime.getRuntime().exec(
minionPath + " -timelimit " + timeout + " " + temp.getAbsolutePath());
// TODO: killer does not work in netbeans. Does it work on CLI?
Runtime.getRuntime().addShutdownHook(new Thread(new MinionKiller(p)));
BufferedReader input =
new BufferedReader(new InputStreamReader(p.getInputStream()));
model = parseMinionOutput(input);
/*while ((line = input.readLine()) != null) {
System.out.println(line);
}*/
input.close();
} catch (Exception err) {
System.err.println("Minion failed to run.");
err.printStackTrace();
}
return model;
}
public boolean isFeasible() {
return foundSolution;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
private class MinionOutputter implements MinionVisitor<String> {
public String visitExpressionList(MinionExpressionList list) {
StringBuffer buff = new StringBuffer();
buff.append("[");
Iterator<MinionExpression> it = list.getList().iterator();
if (it.hasNext()) {
buff.append(it.next().accept(this));
while (it.hasNext()) {
buff.append(",");
buff.append(it.next().accept(this));
}
}
buff.append("]");
return buff.toString();
}
public String visitDomainVariable(MinionDomainVariable v) {
return v.getName();
}
public String visitEnumeratedVariable(MinionEnumeratedVariable v) {
return v.getName();
}
public String visitConstant(MinionExpressionConstant c) {
return new Integer(c.getValue()).toString();
}
}
private class MinionKiller implements Runnable {
private Process minionProcess;
public MinionKiller (Process minonProcess) {
this.minionProcess = minionProcess;
}
public void run () {
//System.err.println("Killing minion ...");
if(minionProcess != null)
minionProcess.destroy();
//System.err.println("Killed minion!");
}
}
}