More AMPL fixes
Change-Id: I824333f8bad128ec936429e06069481db4b335a1
This commit is contained in:
@@ -36,6 +36,7 @@ public class AMPLGenerator {
|
|||||||
|
|
||||||
generateVariablesSection(app, out);
|
generateVariablesSection(app, out);
|
||||||
generateMetricsSection(app, out);
|
generateMetricsSection(app, out);
|
||||||
|
generateConstants(app, out);
|
||||||
generatePerformanceIndicatorsSection(app, out);
|
generatePerformanceIndicatorsSection(app, out);
|
||||||
generateCostParameterSection(app, out);
|
generateCostParameterSection(app, out);
|
||||||
generateUtilityFunctions(app, out);
|
generateUtilityFunctions(app, out);
|
||||||
@@ -44,15 +45,27 @@ public class AMPLGenerator {
|
|||||||
return result.toString();
|
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) {
|
private static void generateConstraints(NebulousApp app, PrintWriter out) {
|
||||||
// We only care about SLOs defined over performance indicators
|
// We only care about SLOs defined over performance indicators
|
||||||
out.println("# Constraints. For constraints we don't have name from GUI, must be created");
|
out.println("# Constraints. For constraints we don't have name from GUI, must be created");
|
||||||
ObjectNode slo = app.getOriginalAppMessage().withObject(NebulousApp.constraints_path);
|
int counter = 0;
|
||||||
Set<String> performance_indicators = app.getPerformanceIndicators().keySet();
|
for (JsonNode slo : app.getEffectiveConstraints()) {
|
||||||
if (!containsPerformanceIndicator(slo, performance_indicators)) return;
|
out.format("subject to constraint_{} : ", counter);
|
||||||
out.print("subject to constraint_0 : ");
|
|
||||||
emitCondition(out, slo);
|
emitCondition(out, slo);
|
||||||
out.println(";");
|
out.println(";");
|
||||||
|
counter = counter + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void emitCondition(PrintWriter out, JsonNode condition){
|
private static void emitCondition(PrintWriter out, JsonNode condition){
|
||||||
@@ -82,22 +95,11 @@ public class AMPLGenerator {
|
|||||||
c.get("value").asText());
|
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) {
|
private static void generateUtilityFunctions(NebulousApp app, PrintWriter out) {
|
||||||
out.println("# Utility functions");
|
out.println("# Utility functions");
|
||||||
for (JsonNode f : app.getUtilityFunctions().values()) {
|
for (JsonNode f : app.getUtilityFunctions().values()) {
|
||||||
|
if (f.get("type").asText().equals("constant"))
|
||||||
|
continue;
|
||||||
String formula = replaceVariables(
|
String formula = replaceVariables(
|
||||||
f.at("/expression/formula").asText(),
|
f.at("/expression/formula").asText(),
|
||||||
f.withArray("/expression/variables"));
|
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");
|
out.println("# Performance indicators = composite metrics that have at least one variable in their formula");
|
||||||
for (final JsonNode m : app.getPerformanceIndicators().values()) {
|
for (final JsonNode m : app.getPerformanceIndicators().values()) {
|
||||||
String name = m.get("name").asText();
|
String name = m.get("name").asText();
|
||||||
String formula = m.get("formula").asText();
|
|
||||||
out.format("var %s;%n", name);
|
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.format("subject to define_%s : %s = %s;%n", name, name, formula);
|
||||||
}
|
}
|
||||||
out.println();
|
out.println();
|
||||||
@@ -152,33 +158,37 @@ public class AMPLGenerator {
|
|||||||
/**
|
/**
|
||||||
* Calculate all metrics that are actually used.
|
* 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.
|
* @param app the NebulousApp.
|
||||||
* @return The set of raw or composite metrics that are used in
|
* @return The set of raw or composite metrics that are used in
|
||||||
* performance indicators, constraints or utility functions.
|
* performance indicators, constraints or utility functions.
|
||||||
*/
|
*/
|
||||||
private static Set<String> usedMetrics(NebulousApp app) {
|
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> result = new HashSet<>();
|
||||||
|
Set<String> allMetrics = new HashSet<>(app.getRawMetrics().keySet());
|
||||||
|
allMetrics.addAll(app.getCompositeMetrics().keySet());
|
||||||
// collect from performance indicators
|
// collect from performance indicators
|
||||||
for (final JsonNode indicator : app.getPerformanceIndicators().values()) {
|
for (final JsonNode indicator : app.getPerformanceIndicators().values()) {
|
||||||
// FIXME: note that here we collect also variables
|
|
||||||
indicator.withArray("arguments").elements()
|
indicator.withArray("arguments").elements()
|
||||||
.forEachRemaining(node -> result.add(node.asText()));
|
.forEachRemaining(node -> {
|
||||||
|
if (allMetrics.contains(node.asText()))
|
||||||
|
result.add(node.asText());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// collect from constraints
|
// collect from constraints
|
||||||
ObjectNode slo = app.getOriginalAppMessage().withObject(NebulousApp.constraints_path);
|
for (JsonNode slo : app.getEffectiveConstraints()) {
|
||||||
slo.findValuesAsText("metricName").forEach(result::add);
|
slo.findValuesAsText("metricName").forEach(metricName -> {
|
||||||
|
if (allMetrics.contains(metricName)) result.add(metricName);
|
||||||
|
});
|
||||||
|
}
|
||||||
// collect from utility functions
|
// collect from utility functions
|
||||||
for (JsonNode function : app.getUtilityFunctions().values()) {
|
for (JsonNode function : app.getUtilityFunctions().values()) {
|
||||||
function.withArray("/expression/variables")
|
function.withArray("/expression/variables")
|
||||||
.findValuesAsText("value")
|
.findValuesAsText("value")
|
||||||
.forEach(result::add);
|
.forEach(name -> {
|
||||||
|
if (allMetrics.contains(name)) result.add(name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -92,6 +93,12 @@ public class NebulousApp {
|
|||||||
@Getter private Map<String, JsonNode> performanceIndicators = new HashMap<>();
|
@Getter private Map<String, JsonNode> performanceIndicators = new HashMap<>();
|
||||||
/** The app's utility functions; the AMPL solver will optimize for one of these. */
|
/** The app's utility functions; the AMPL solver will optimize for one of these. */
|
||||||
@Getter private Map<String, JsonNode> utilityFunctions = new HashMap<>();
|
@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 */
|
/** When an app gets deployed or redeployed, this is where we send the AMPL file */
|
||||||
private Publisher ampl_message_channel;
|
private Publisher ampl_message_channel;
|
||||||
/** Have we ever been deployed? I.e., when we rewrite KubeVela, are there
|
/** 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.
|
// What's left is neither a raw nor composite metric.
|
||||||
utilityFunctions.put(f.get("name").asText(), f);
|
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);
|
log.debug("New App instantiated: Name='{}', UUID='{}'", name, UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user