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