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
	 Rudi Schlatte
					Rudi Schlatte