More AMPL fixes
Change-Id: I824333f8bad128ec936429e06069481db4b335a1
This commit is contained in:
@@ -36,6 +36,7 @@ public class AMPLGenerator {
|
||||
|
||||
generateVariablesSection(app, out);
|
||||
generateMetricsSection(app, out);
|
||||
generateConstants(app, out);
|
||||
generatePerformanceIndicatorsSection(app, out);
|
||||
generateCostParameterSection(app, out);
|
||||
generateUtilityFunctions(app, out);
|
||||
@@ -44,15 +45,27 @@ public class AMPLGenerator {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// Utility functions that are constant go here
|
||||
private static void generateConstants(NebulousApp app, PrintWriter out) {
|
||||
out.println("# Constants");
|
||||
for (JsonNode f : app.getUtilityFunctions().values()) {
|
||||
if (!(f.get("type").asText().equals("constant")))
|
||||
continue;
|
||||
out.format("param %s;%n", f.get("name").asText());
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
|
||||
private static void generateConstraints(NebulousApp app, PrintWriter out) {
|
||||
// We only care about SLOs defined over performance indicators
|
||||
out.println("# Constraints. For constraints we don't have name from GUI, must be created");
|
||||
ObjectNode slo = app.getOriginalAppMessage().withObject(NebulousApp.constraints_path);
|
||||
Set<String> performance_indicators = app.getPerformanceIndicators().keySet();
|
||||
if (!containsPerformanceIndicator(slo, performance_indicators)) return;
|
||||
out.print("subject to constraint_0 : ");
|
||||
emitCondition(out, slo);
|
||||
out.println(";");
|
||||
int counter = 0;
|
||||
for (JsonNode slo : app.getEffectiveConstraints()) {
|
||||
out.format("subject to constraint_{} : ", counter);
|
||||
emitCondition(out, slo);
|
||||
out.println(";");
|
||||
counter = counter + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitCondition(PrintWriter out, JsonNode condition){
|
||||
@@ -82,22 +95,11 @@ public class AMPLGenerator {
|
||||
c.get("value").asText());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We only want to emit constraints over at least one performance
|
||||
* indicator. Those have the key of a performance indicator in a "key"
|
||||
* field.
|
||||
*/
|
||||
private static boolean containsPerformanceIndicator (JsonNode constraint, Set<String> performance_indicators) {
|
||||
for (String key : constraint.findValuesAsText("metricName")) {
|
||||
if (performance_indicators.contains(key)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void generateUtilityFunctions(NebulousApp app, PrintWriter out) {
|
||||
out.println("# Utility functions");
|
||||
for (JsonNode f : app.getUtilityFunctions().values()) {
|
||||
if (f.get("type").asText().equals("constant"))
|
||||
continue;
|
||||
String formula = replaceVariables(
|
||||
f.at("/expression/formula").asText(),
|
||||
f.withArray("/expression/variables"));
|
||||
@@ -119,8 +121,12 @@ public class AMPLGenerator {
|
||||
out.println("# Performance indicators = composite metrics that have at least one variable in their formula");
|
||||
for (final JsonNode m : app.getPerformanceIndicators().values()) {
|
||||
String name = m.get("name").asText();
|
||||
String formula = m.get("formula").asText();
|
||||
out.format("var %s;%n", name);
|
||||
}
|
||||
out.println("# Performance indicator formulas");
|
||||
for (final JsonNode m : app.getPerformanceIndicators().values()) {
|
||||
String name = m.get("name").asText();
|
||||
String formula = m.get("formula").asText();
|
||||
out.format("subject to define_%s : %s = %s;%n", name, name, formula);
|
||||
}
|
||||
out.println();
|
||||
@@ -152,33 +158,37 @@ public class AMPLGenerator {
|
||||
/**
|
||||
* Calculate all metrics that are actually used.
|
||||
*
|
||||
* NOTE: may also contain variable names. This is not a problem as long
|
||||
* as we use the result of this method to filter out unused metrics, but
|
||||
* must be fixed if we use this method for other purposes as well.
|
||||
*
|
||||
* @param app the NebulousApp.
|
||||
* @return The set of raw or composite metrics that are used in
|
||||
* performance indicators, constraints or utility functions.
|
||||
*/
|
||||
private static Set<String> usedMetrics(NebulousApp app) {
|
||||
// TODO: should we also add metrics that are used in other metrics
|
||||
// that are used elsewhere, or does the solver take care of that?
|
||||
Set<String> result = new HashSet<>();
|
||||
Set<String> allMetrics = new HashSet<>(app.getRawMetrics().keySet());
|
||||
allMetrics.addAll(app.getCompositeMetrics().keySet());
|
||||
// collect from performance indicators
|
||||
for (final JsonNode indicator : app.getPerformanceIndicators().values()) {
|
||||
// FIXME: note that here we collect also variables
|
||||
indicator.withArray("arguments").elements()
|
||||
.forEachRemaining(node -> result.add(node.asText()));
|
||||
.forEachRemaining(node -> {
|
||||
if (allMetrics.contains(node.asText()))
|
||||
result.add(node.asText());
|
||||
});
|
||||
}
|
||||
// collect from constraints
|
||||
ObjectNode slo = app.getOriginalAppMessage().withObject(NebulousApp.constraints_path);
|
||||
slo.findValuesAsText("metricName").forEach(result::add);
|
||||
for (JsonNode slo : app.getEffectiveConstraints()) {
|
||||
slo.findValuesAsText("metricName").forEach(metricName -> {
|
||||
if (allMetrics.contains(metricName)) result.add(metricName);
|
||||
});
|
||||
}
|
||||
// collect from utility functions
|
||||
for (JsonNode function : app.getUtilityFunctions().values()) {
|
||||
function.withArray("/expression/variables")
|
||||
.findValuesAsText("value")
|
||||
.forEach(result::add);
|
||||
.forEach(name -> {
|
||||
if (allMetrics.contains(name)) result.add(name);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -92,6 +93,12 @@ public class NebulousApp {
|
||||
@Getter private Map<String, JsonNode> performanceIndicators = new HashMap<>();
|
||||
/** The app's utility functions; the AMPL solver will optimize for one of these. */
|
||||
@Getter private Map<String, JsonNode> utilityFunctions = new HashMap<>();
|
||||
/**
|
||||
* The constraints that are actually effective: if a constraint does not
|
||||
* contain a variable, we cannot influence it via the solver
|
||||
*/
|
||||
@Getter private Set<JsonNode> effectiveConstraints = new HashSet<>();
|
||||
|
||||
/** When an app gets deployed or redeployed, this is where we send the AMPL file */
|
||||
private Publisher ampl_message_channel;
|
||||
/** Have we ever been deployed? I.e., when we rewrite KubeVela, are there
|
||||
@@ -167,6 +174,18 @@ public class NebulousApp {
|
||||
// What's left is neither a raw nor composite metric.
|
||||
utilityFunctions.put(f.get("name").asText(), f);
|
||||
}
|
||||
// In the current app message, constraints is not an array. When this
|
||||
// changes, wrap this for loop in another loop over the constraints
|
||||
// (Constraints are called sloViolations in the app message).
|
||||
for (String key : app_message.withObject(constraints_path).findValuesAsText("metricName")) {
|
||||
// Constraints that do not use variables, directly or via
|
||||
// performance indicators, will be ignored.
|
||||
if (kubevela_variable_paths.keySet().contains(key)
|
||||
|| performanceIndicators.keySet().contains(key)) {
|
||||
effectiveConstraints.add(app_message.withObject(constraints_path));
|
||||
break;
|
||||
}
|
||||
}
|
||||
log.debug("New App instantiated: Name='{}', UUID='{}'", name, UUID);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user