V1_0.java [src/java/m/multiobj] Revision: Date:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package m.multiobj;
import csip.ModelDataService;
import csip.annotations.*;
import static csip.annotations.ResourceType.OUTPUT;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Path;
import org.rythmengine.Rythm;
import org.freshvanilla.compile.CachedCompiler;
import org.freshvanilla.compile.CompilerUtils;
import org.moeaframework.Executor;
import org.moeaframework.core.NondominatedPopulation;
import org.moeaframework.core.Solution;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.moeaframework.core.Variable;
import org.moeaframework.core.variable.EncodingUtils;
/**
* A playground and testbed for performing multi-objective analysis.
*
* @author Andre Dozier
*/
@Name("multiobj")
@Description("Multiobjective playground")
@Resource(file = "/bin/moea.jar", id = "moea")
@Resource(file = "/templates/Problem.template", id = "prob")
@Resource(file = "*.java", type = OUTPUT)
@Path("m/mo/1.0")
public class V1_0 extends ModelDataService {
private final static DateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmssSSS");
NondominatedPopulation result;
Class<?> cls;
String method; // = "NSGAII";
int max_evals; // = 10000;
int population; // = 50;
int nDecisions, nObjectives, nConstraints;
JSONArray vars;
String code;
String generatedCode;
Map<String, Object> params;
private Map<String, Object> getTemplateParams(JSONArray vars, String className, int nDecisions, int nObjectives, int nConstraints, String code) throws JSONException {
Map<String, Object> retVal = new HashMap<>(6);
// classname
retVal.put("classname", className);
// problem definition
retVal.put("nDecisions", nDecisions);
retVal.put("nObjectives", nObjectives);
retVal.put("nConstraints", nConstraints);
// variable definitions
JSONObject o;
if (vars.length() != nDecisions) {
throw new IllegalArgumentException("Number of 'vars' must equal 'nDecisions'");
}
String[] varType = new String[nDecisions];
int[] varNbits = new int[nDecisions];
double[] varLower = new double[nDecisions];
double[] varUpper = new double[nDecisions];
for (int i = 0; i < nDecisions; i++) {
o = vars.getJSONObject(i);
// variable type
varType[i] = o.getString("type").toLowerCase();
if (!(varType[i].equals("boolean")
|| varType[i].equals("binary")
|| varType[i].equals("permutation")
|| varType[i].equals("discrete")
|| varType[i].equals("real"))) {
throw new IllegalArgumentException("Type must be 'boolean', 'binary', 'permutation', discrete', or 'real'. Got '" + varType[i] + "'!");
}
// binary variable - number of bits
if (o.has("nbits")) {
varNbits[i] = o.getInt("nbits");
} else if (varType[i].equals("binary") || varType[i].equals("permutation")) {
throw new IllegalArgumentException("binary and permutation variable types require 'nbits' property!");
}
// lower bounds
if (o.has("lower")) {
varLower[i] = o.getDouble("lower");
} else if (varType[i].equals("discrete") || varType[i].equals("real")) {
throw new IllegalArgumentException("discrete or real variables require 'lower' properties!");
}
// upper bounds
if (o.has("upper")) {
varUpper[i] = o.getDouble("upper");
} else if (varType[i].equals("discrete") || varType[i].equals("real")) {
throw new IllegalArgumentException("discrete or real variables require 'upper' properties!");
}
}
retVal.put("varType", varType);
retVal.put("varNbits", varNbits);
retVal.put("varLower", varLower);
retVal.put("varUpper", varUpper);
// user-supplied function evaluation code (and constraints)
retVal.put("code", code);
return retVal;
}
private JSONObject objectives() throws JSONException {
int n = nObjectives;
int size = result.size();
JSONObject o = new JSONObject();
double[][] f = new double[n][size];
o.put("n", n);
o.put("size", size);
int i = -1;
for (Solution solution : result) {
i++;
for (int j = 0; j < n; j++) {
f[j][i] = solution.getObjective(j);
}
}
for (int j = 0; j < n; j++) {
String name = "f" + j;
JSONObject fj = new JSONObject();
fj.put("name", name);
fj.put("values", JSON.toJSONArray(f[j]));
o.put(name, fj);
}
return o;
}
private JSONObject constraints() throws JSONException {
int n = nConstraints;
int size = result.size();
JSONObject o = new JSONObject();
double[][] c = new double[n][size];
o.put("n", n);
o.put("size", size);
int i = -1;
for (Solution solution : result) {
i++;
for (int j = 0; j < n; j++) {
c[j][i] = solution.getConstraint(j);
}
}
for (int j = 0; j < n; j++) {
String name = "c" + j;
JSONObject cj = new JSONObject();
cj.put("name", name);
cj.put("values", JSON.toJSONArray(c[j]));
o.put(name, cj);
}
return o;
}
private JSONObject decisions() throws JSONException {
int n = nDecisions;
int size = result.size();
JSONObject o = new JSONObject();
String[][] x = new String[n][size];
o.put("n", n);
o.put("size", size);
int i = -1;
for (Solution solution : result) {
i++;
for (int j = 0; j < n; j++) {
Variable v = solution.getVariable(j);
String varType = vars.getJSONObject(j).getString("type").toLowerCase();
switch (varType) {
case "boolean":
x[j][i] = String.valueOf(EncodingUtils.getBoolean(v));
break;
case "binary":
x[j][i] = EasyArray.toBinaryString(EncodingUtils.getBinary(v));
break;
case "permutation":
x[j][i] = String.join(" ", EasyArray.toString(EncodingUtils.getPermutation(v)));
break;
case "discrete":
x[j][i] = String.valueOf(EncodingUtils.getInt(v));
break;
case "real":
x[j][i] = String.valueOf(EncodingUtils.getReal(v));
break;
default:
throw new IllegalArgumentException("Variable type is not recognized '" + varType + "'!");
}
}
}
for (int j = 0; j < n; j++) {
String name = "x" + j;
JSONObject xj = JSON.copy(vars.getJSONObject(j));
xj.put("name", name);
xj.put("values", JSON.toJSONArray(x[j]));
o.put(name, xj);
}
return o;
}
private JSONObject output() throws JSONException {
JSONObject o = new JSONObject();
o.put("size", result.size());
o.put("nObjectives", nObjectives);
o.put("nConstraints", nConstraints);
o.put("nDecisions", nDecisions);
o.put("objectives", objectives());
o.put("constraints", constraints());
o.put("decisions", decisions());
return o;
}
/**
* Compile the code into a Java class
*
* @throws Exception
*/
@Override
protected void preProcess() throws Exception {
// add moea to class path
File f = getResourceFile("moea");
CompilerUtils.addClassPath(f.toString());
CachedCompiler cc = CompilerUtils.CACHED_COMPILER;
// get algorithm parameters
method = getStringParam("method");
max_evals = getIntParam("max_evals");
population = getIntParam("population");
// problem parameters
nDecisions = getIntParam("nDecisions");
nObjectives = getIntParam("nObjectives");
nConstraints = getIntParam("nConstraints");
vars = getJSONArrayParam("vars");
code = getStringParam("code");
// generate the code
String className = "Prob_" + fmt.format(new Date());
params = getTemplateParams(vars, className, nDecisions, nObjectives, nConstraints, code);
// render the code
File p = getResourceFile("prob");
generatedCode = Rythm.render(p, params);
// save code to file
// File genCodeFile = new File(getWorkspaceDir(), "Problem.java");
// FileUtils.writeStringToFile(genCodeFile, generatedCode);
// LOG.info(code);
// compile the code
cls = cc.loadFromJava(className, generatedCode);
}
/**
* Execute the multi-objective problem
*
* @return
* @throws Exception
*/
@Override
protected void doProcess() throws Exception {
result = new Executor()
.withProblemClass(cls)
.withAlgorithm(method)
.withMaxEvaluations(max_evals)
.withProperty("populationSize", population)
.run();
}
/**
* Place the multi-objective optimization output into the result
*
* @throws Exception
*/
@Override
protected void postProcess() throws Exception {
// add summary info
putResult("output", output());
putResult("Problem.java", generatedCode);
}
}