diff --git a/experiments/src/metrics/ASUtilityGenerator/ASUtilityGenerator.py b/experiments/src/metrics/ASUtilityGenerator/ASUtilityGenerator.py new file mode 100644 index 0000000000000000000000000000000000000000..5c78fabad18c84944cdbf3023b068b3869c15c83 --- /dev/null +++ b/experiments/src/metrics/ASUtilityGenerator/ASUtilityGenerator.py @@ -0,0 +1,92 @@ +import jnius_config +# the jar should contain the (upperware) utility generator library and the ASUtilityGeneratorApplication from java-src (with dependencies) +jnius_config.set_classpath("path to utility-generator-jar-with-dependencies.jar") + +from jnius import autoclass +from jnius import JavaClass, MetaJavaClass +from pathlib import Path +from tempfile import NamedTemporaryFile +from typing import List, Dict +from xml.etree.ElementTree import ElementTree, Element +from lxml import etree +from os import unlink + +ASUtilityGeneratorApplication = autoclass("eu.melodic.upperware.utilitygenerator.ASUtilityGeneratorApplication") +IntVariableValueDTO = autoclass("eu.melodic.upperware.utilitygenerator.cdo.cp_model.DTO.IntVariableValueDTO") +ArrayList = autoclass("java.util.ArrayList") + +class ASUtilityGenerator: + """ + Class used for utilizing the "utility generator" java library to calculate the utility function value + for given constraint problem and camel model. + + Requires java library with utility generator (and its dependencies) and ASUtilityGeneratorApplication from java-src. + The .jar containing the library should be included in the classpath declared at the beginning of this file. + """ + _cp_model: ElementTree + _xml_fillings: Dict[str, Element] + + _cp_model_path: str + _camel_model_path: str + _node_candidates_path: str + + def __init__(self, + cp_model_path: str, + camel_model_path: str, + node_candidates_path: str, + metric_names: List[str]): + """ + :param cp_model_path: path to the file containing the constraint problem model + :param camel_model_path: path to the file containing the camel model + :param node_candidates_path: path to the file containing node candidates + :param metric_names: names of the metrics that might be changed (exactly as they appear in the cp model file) + """ + + self._cp_model_path = Path(cp_model_path) + self._cp_model = etree.parse(str(Path(self._cp_model_path))) + self._camel_model_path = camel_model_path + self._node_candidates_path = node_candidates_path + + self._xml_fillings = {} + for name in metric_names: + self._xml_fillings[name] = self._cp_model.find( + f"cpMetrics[@id='{name}']")[0] + + def _add_metrics(self, filename, metrics): + """ + Adds metrics to the constraint problem model. + :param filename: name of the file containing the constraint problem model + :param metrics: dictionary with pairs (arg_name, arg_value) describing the metrics to be added. If metrics are + empty, then no value wil be changed + """ + for arg_name, arg_value in metrics.items(): + arg_loc = self._xml_fillings[arg_name] + arg_loc.set('value', str(arg_value)) + + self._cp_model.write(filename, + xml_declaration=True, + encoding="ASCII") + + def evaluate(self, configuration, metrics) -> float: + """ + Creates java objects based on the parameters and a tmeporary file with an updated contraint problem model. + Then it calculates the utility function value using the java's ASUtilityGeneratorApplication. + :param configuration: dictionary with pairs (arg_name, arg_value) describing the configuration + :param metrics: dictionary with pairs (arg_name, arg_value) describing the metrics + :return: the utility function value for the given parameters + """ + tempfile = NamedTemporaryFile(delete=False) + + self._add_metrics(filename = tempfile.name, metrics=metrics) + variable_list = ArrayList() + for (name, value) in configuration.items(): + variable_list.add(IntVariableValueDTO(name, round(value))) + + utility_generator = ASUtilityGeneratorApplication(self._camel_model_path, tempfile.name, + self._node_candidates_path) + utility_value = utility_generator.evaluate(variable_list) + + tempfile.close() + unlink(tempfile.name) + + return utility_value