From 895b26df7a806a2136c2f7a46d56b542326e561f Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Wed, 6 Sep 2023 19:05:10 +0200 Subject: feat(dse): transformation rule builder --- .../store-dse-visualization/build.gradle.kts | 13 + .../visualization/ModelVisualizerAdapter.java | 32 ++ .../visualization/ModelVisualizerBuilder.java | 16 + .../visualization/ModelVisualizerStoreAdapter.java | 22 ++ .../visualization/internal/FileFormat.java | 26 ++ .../internal/ModelVisualizeStoreAdapterImpl.java | 60 ++++ .../internal/ModelVisualizerAdapterImpl.java | 387 +++++++++++++++++++++ .../internal/ModelVisualizerBuilderImpl.java | 55 +++ .../tools/refinery/store/dse/ActionFactory.java | 15 - .../store/dse/modification/DanglingEdges.java | 12 + .../dse/modification/ModificationAdapter.java | 2 +- .../modification/actions/CreateActionLiteral.java | 43 +++ .../modification/actions/DeleteActionLiteral.java | 51 +++ .../actions/ModificationActionLiterals.java | 23 ++ .../internal/ModificationAdapterImpl.java | 55 ++- .../transition/DesignSpaceExplorationBuilder.java | 8 +- .../DesignSpaceExplorationStoreAdapter.java | 2 +- .../tools/refinery/store/dse/transition/Rule.java | 99 ++++++ .../refinery/store/dse/transition/RuleBuilder.java | 71 ++++ .../store/dse/transition/Transformation.java | 25 +- .../store/dse/transition/TransformationRule.java | 63 ---- .../transition/actions/AbstractActionLiteral.java | 9 + .../store/dse/transition/actions/Action.java | 132 +++++++ .../dse/transition/actions/ActionLiteral.java | 19 + .../dse/transition/actions/ActionLiterals.java | 33 ++ .../store/dse/transition/actions/BoundAction.java | 109 ++++++ .../dse/transition/actions/BoundActionLiteral.java | 16 + .../dse/transition/actions/PutActionLiteral.java | 64 ++++ .../dse/transition/callback/ActionCallback0.java | 15 + .../dse/transition/callback/ActionCallback1.java | 16 + .../dse/transition/callback/ActionCallback2.java | 16 + .../dse/transition/callback/ActionCallback3.java | 16 + .../dse/transition/callback/ActionCallback4.java | 16 + .../dse/transition/callback/RuleCallback0.java | 13 + .../dse/transition/callback/RuleCallback1.java | 14 + .../dse/transition/callback/RuleCallback2.java | 14 + .../dse/transition/callback/RuleCallback3.java | 14 + .../dse/transition/callback/RuleCallback4.java | 14 + .../DesignSpaceExplorationBuilderImpl.java | 30 +- .../DesignSpaceExplorationStoreAdapterImpl.java | 32 +- .../store/dse/transition/objectives/Criterion.java | 2 +- .../store/dse/transition/objectives/Objective.java | 2 +- .../dse/transition/objectives/QueryCriteria.java | 4 +- .../dse/transition/objectives/QueryObjective.java | 4 +- .../tools/refinery/store/dse/CRAExamplesTest.java | 54 ++- subprojects/store-reasoning/build.gradle.kts | 2 +- subprojects/visualization/build.gradle.kts | 13 - .../visualization/ModelVisualizerAdapter.java | 32 -- .../visualization/ModelVisualizerBuilder.java | 16 - .../visualization/ModelVisualizerStoreAdapter.java | 22 -- .../visualization/internal/FileFormat.java | 26 -- .../internal/ModelVisualizeStoreAdapterImpl.java | 60 ---- .../internal/ModelVisualizerAdapterImpl.java | 387 --------------------- .../internal/ModelVisualizerBuilderImpl.java | 55 --- 54 files changed, 1575 insertions(+), 776 deletions(-) create mode 100644 subprojects/store-dse-visualization/build.gradle.kts create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java create mode 100644 subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java delete mode 100644 subprojects/visualization/build.gradle.kts delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java delete mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java diff --git a/subprojects/store-dse-visualization/build.gradle.kts b/subprojects/store-dse-visualization/build.gradle.kts new file mode 100644 index 00000000..abad0491 --- /dev/null +++ b/subprojects/store-dse-visualization/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ + +plugins { + id("tools.refinery.gradle.java-library") +} + +dependencies { + api(project(":refinery-store-query")) +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java new file mode 100644 index 00000000..ae87d8ac --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.map.Version; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; + +import java.util.Collection; + +public interface ModelVisualizerAdapter extends ModelAdapter { + + ModelVisualizerStoreAdapter getStoreAdapter(); + static ModelVisualizerBuilder builder() { + return new ModelVisualizerBuilderImpl(); + } + + public void addTransition(Version from, Version to, String action); + + + public void addTransition(Version from, Version to, String action, Tuple activation); + public void addState(Version state); + public void addState(Version state, Collection fitness); + public void addState(Version state, String label); + public void addSolution(Version state); + public void visualize(); + +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java new file mode 100644 index 00000000..592f5fcf --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.visualization.internal.FileFormat; + +public interface ModelVisualizerBuilder extends ModelAdapterBuilder { + ModelVisualizerBuilder withOutputpath(String outputpath); + ModelVisualizerBuilder withFormat(FileFormat format); + ModelVisualizerBuilder saveDesignSpace(); + ModelVisualizerBuilder saveStates(); +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java new file mode 100644 index 00000000..46663b2a --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.visualization.internal.FileFormat; + +import java.util.Set; + +public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { + + String getOutputPath(); + + boolean isRenderDesignSpace(); + + boolean isRenderStates(); + + Set getFormats(); +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java new file mode 100644 index 00000000..c5dffeb2 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +public enum FileFormat { + BMP("bmp"), + DOT("dot"), + JPEG("jpg"), + PDF("pdf"), + PLAIN("plain"), + PNG("png"), + SVG("svg"); + + private final String format; + + FileFormat(String format) { + this.format = format; + } + + public String getFormat() { + return format; + } +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java new file mode 100644 index 00000000..04be22d6 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; + +import java.util.Set; + +public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter { + private final ModelStore store; + private final String outputPath; + private final boolean renderDesignSpace; + private final boolean renderStates; + private final Set formats; + + public ModelVisualizeStoreAdapterImpl(ModelStore store, String outputPath, Set formats, + boolean renderDesignSpace, boolean renderStates) { + this.store = store; + this.outputPath = outputPath; + this.formats = formats; + this.renderDesignSpace = renderDesignSpace; + this.renderStates = renderStates; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return new ModelVisualizerAdapterImpl(model, this); + } + + @Override + public String getOutputPath() { + return outputPath; + } + + @Override + public boolean isRenderDesignSpace() { + return renderDesignSpace; + } + + @Override + public boolean isRenderStates() { + return renderStates; + } + + @Override + public Set getFormats() { + return formats; + } +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java new file mode 100644 index 00000000..531969b4 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -0,0 +1,387 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +import tools.refinery.store.map.Version; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.representation.AnySymbol; +import tools.refinery.store.representation.TruthValue; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; + +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; + +public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { + private final Model model; + private final ModelVisualizerStoreAdapter storeAdapter; + private final Map> allInterpretations; + private final StringBuilder designSpaceBuilder = new StringBuilder(); + private final Map states = new HashMap<>(); + private int transitionCounter = 0; + private Integer numberOfStates = 0; + private final String outputPath; + private final Set formats; + private final boolean renderDesignSpace; + private final boolean renderStates; + + private static final Map truthValueToDot = Map.of( + TruthValue.TRUE, "1", + TruthValue.FALSE, "0", + TruthValue.UNKNOWN, "½", + TruthValue.ERROR, "E", + true, "1", + false, "0" + ); + + public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { + this.model = model; + this.storeAdapter = storeAdapter; + this.outputPath = storeAdapter.getOutputPath(); + this.formats = storeAdapter.getFormats(); + if (formats.isEmpty()) { + formats.add(FileFormat.SVG); + } + this.renderDesignSpace = storeAdapter.isRenderDesignSpace(); + this.renderStates = storeAdapter.isRenderStates(); + + this.allInterpretations = new HashMap<>(); + for (var symbol : storeAdapter.getStore().getSymbols()) { + var arity = symbol.arity(); + if (arity < 1 || arity > 2) { + continue; + } + var interpretation = (Interpretation) model.getInterpretation(symbol); + allInterpretations.put(symbol, interpretation); + } + designSpaceBuilder.append("digraph designSpace {\n"); + designSpaceBuilder.append(""" + nodesep=0 + ranksep=5 + node[ + \tstyle=filled + \tfillcolor=white + ] + """); + } + + @Override + public Model getModel() { + return model; + } + + @Override + public ModelVisualizerStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + private String createDotForCurrentModelState() { + + var unaryTupleToInterpretationsMap = new HashMap>>(); + + var sb = new StringBuilder(); + + sb.append("digraph model {\n"); + sb.append(""" + node [ + \tstyle="filled, rounded" + \tshape=plain + \tpencolor="#00000088" + \tfontname="Helvetica" + ] + """); + sb.append(""" + edge [ + \tlabeldistance=3 + \tfontname="Helvetica" + ] + """); + + for (var entry : allInterpretations.entrySet()) { + var key = entry.getKey(); + var arity = key.arity(); + var cursor = entry.getValue().getAll(); + if (arity == 1) { + while (cursor.move()) { + unaryTupleToInterpretationsMap.computeIfAbsent(cursor.getKey(), k -> new LinkedHashSet<>()) + .add(entry.getValue()); + } + } else if (arity == 2) { + while (cursor.move()) { + var tuple = cursor.getKey(); + for (var i = 0; i < tuple.getSize(); i++) { + var id = tuple.get(i); + unaryTupleToInterpretationsMap.computeIfAbsent(Tuple.of(id), k -> new LinkedHashSet<>()); + } + sb.append(drawEdge(cursor.getKey(), key, entry.getValue())); + } + } + } + for (var entry : unaryTupleToInterpretationsMap.entrySet()) { + sb.append(drawElement(entry)); + } + sb.append("}"); + return sb.toString(); + } + + private StringBuilder drawElement(Map.Entry>> entry) { + var sb = new StringBuilder(); + + var tableStyle = " CELLSPACING=\"0\" BORDER=\"2\" CELLBORDER=\"0\" CELLPADDING=\"4\" STYLE=\"ROUNDED\""; + + var key = entry.getKey(); + var id = key.get(0); + var mainLabel = String.valueOf(id); + var interpretations = entry.getValue(); + var backgroundColor = toBackgroundColorString(averageColor(interpretations)); + + sb.append(id); + sb.append(" [\n"); + sb.append("\tfillcolor=\"").append(backgroundColor).append("\"\n"); + sb.append("\tlabel="); + if (interpretations.isEmpty()) { + sb.append("<\n\t").append(mainLabel).append(""); + } + else { + sb.append("<\n\t\t") + .append(mainLabel).append("\n"); + for (var interpretation : interpretations) { + var rawValue = interpretation.get(key); + + if (rawValue == null || rawValue.equals(TruthValue.FALSE) || rawValue.equals(false)) { + continue; + } + var color = "black"; + if (rawValue.equals(TruthValue.ERROR)) { + color = "red"; + } + var value = truthValueToDot.getOrDefault(rawValue, rawValue.toString()); + var symbol = interpretation.getSymbol(); + + if (symbol.valueType() == String.class) { + value = "\"" + value + "\""; + } + sb.append("\t\t") + .append(interpretation.getSymbol().name()) + .append("") + .append("=").append(value) + .append("\n"); + } + } + sb.append("\t\t>\n"); + sb.append("]\n"); + + return sb; + } + + private String drawEdge(Tuple edge, AnySymbol symbol, Interpretation interpretation) { + var value = interpretation.get(edge); + + if (value == null || value.equals(TruthValue.FALSE) || value.equals(false)) { + return ""; + } + + var sb = new StringBuilder(); + var style = "solid"; + var color = "black"; + if (value.equals(TruthValue.UNKNOWN)) { + style = "dotted"; + } + else if (value.equals(TruthValue.ERROR)) { + style = "dashed"; + color = "red"; + } + + var from = edge.get(0); + var to = edge.get(1); + var name = symbol.name(); + sb.append(from).append(" -> ").append(to) + .append(" [\n\tstyle=").append(style) + .append("\n\tcolor=").append(color) + .append("\n\tfontcolor=").append(color) + .append("\n\tlabel=\"").append(name) + .append("\"]\n"); + return sb.toString(); + } + + private String toBackgroundColorString(Integer[] backgroundColor) { + if (backgroundColor.length == 3) + return String.format("#%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2]); + else if (backgroundColor.length == 4) + return String.format("#%02x%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2], + backgroundColor[3]); + return null; + } + + private Integer[] typeColor(String name) { + var random = new Random(name.hashCode()); + return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 }; + } + + private Integer[] averageColor(Set> interpretations) { + if(interpretations.isEmpty()) { + return new Integer[]{256, 256, 256}; + } + // TODO: Only use interpretations where the value is not false (or unknown) + var symbols = interpretations.stream() + .map(i -> typeColor(i.getSymbol().name())).toArray(Integer[][]::new); + + + + return new Integer[] { + Arrays.stream(symbols).map(i -> i[0]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), + Arrays.stream(symbols).map(i -> i[1]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), + Arrays.stream(symbols).map(i -> i[2]).collect(Collectors.averagingInt(Integer::intValue)).intValue() + }; + } + + private String createDotForModelState(Version version) { + var currentVersion = model.getState(); + model.restore(version); + var graph = createDotForCurrentModelState(); + model.restore(currentVersion); + return graph; + } + + private boolean saveDot(String dot, String filePath) { + File file = new File(filePath); + file.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(dot); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + private boolean renderDot(String dot, String filePath) { + return renderDot(dot, FileFormat.SVG, filePath); + } + + private boolean renderDot(String dot, FileFormat format, String filePath) { + try { + Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start(); + + OutputStream osToProcess = process.getOutputStream(); + PrintWriter pwToProcess = new PrintWriter(osToProcess); + pwToProcess.write(dot); + pwToProcess.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public void addTransition(Version from, Version to, String action) { + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n"); + } + + @Override + public void addTransition(Version from, Version to, String action, Tuple activation) { + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / "); + + + for (int i = 0; i < activation.getSize(); i++) { + designSpaceBuilder.append(activation.get(i)); + if (i < activation.getSize() - 1) { + designSpaceBuilder.append(", "); + } + } + designSpaceBuilder.append("\"]\n"); + } + + @Override + public void addState(Version state) { + if (states.containsKey(state)) { + return; + } + states.put(state, numberOfStates++); + designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); + } + + @Override + public void addState(Version state, Collection fitness) { + var labelBuilder = new StringBuilder(); + for (var f : fitness) { + labelBuilder.append(f).append(", "); + } + addState(state, labelBuilder.toString()); + } + + @Override + public void addState(Version state, String label) { + if (states.containsKey(state)) { + return; + } + states.put(state, numberOfStates++); + designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); + designSpaceBuilder.append(label); + designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); + } + + @Override + public void addSolution(Version state) { + addState(state); + designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n"); + } + + private String buildDesignSpaceDot() { + designSpaceBuilder.append("}"); + return designSpaceBuilder.toString(); + } + + private boolean saveDesignSpace(String path) { + saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); + for (var entry : states.entrySet()) { + saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot"); + } + return true; + } + + private void renderDesignSpace(String path, Set formats) { + File filePath = new File(path); + filePath.mkdirs(); + if (renderStates) { + for (var entry : states.entrySet()) { + var stateId = entry.getValue(); + var stateDot = createDotForModelState(entry.getKey()); + for (var format : formats) { + if (format == FileFormat.DOT) { + saveDot(stateDot, path + "/" + stateId + ".dot"); + } + else { + renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); + } + } + } + } + if (renderDesignSpace) { + var designSpaceDot = buildDesignSpaceDot(); + for (var format : formats) { + if (format == FileFormat.DOT) { + saveDot(designSpaceDot, path + "/designSpace.dot"); + } + else { + renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); + } + } + } + } + + @Override + public void visualize() { + renderDesignSpace(outputPath, formats); + } +} diff --git a/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java new file mode 100644 index 00000000..e4d801d8 --- /dev/null +++ b/subprojects/store-dse-visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.visualization.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.model.ModelStore; +import tools.refinery.visualization.ModelVisualizerBuilder; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class ModelVisualizerBuilderImpl + extends AbstractModelAdapterBuilder + implements ModelVisualizerBuilder { + private String outputPath; + private boolean saveDesignSpace = false; + private boolean saveStates = false; + private Set formats = new LinkedHashSet<>(); + + @Override + protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { + return new ModelVisualizeStoreAdapterImpl(store, outputPath, formats, saveDesignSpace, saveStates); + } + + @Override + public ModelVisualizerBuilder withOutputpath(String outputpath) { + checkNotConfigured(); + this.outputPath = outputpath; + return this; + } + + @Override + public ModelVisualizerBuilder withFormat(FileFormat format) { + checkNotConfigured(); + this.formats.add(format); + return this; + } + + @Override + public ModelVisualizerBuilder saveDesignSpace() { + checkNotConfigured(); + this.saveDesignSpace = true; + return this; + } + + @Override + public ModelVisualizerBuilder saveStates() { + checkNotConfigured(); + this.saveStates = true; + return this; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java deleted file mode 100644 index 48a508b4..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.model.Model; -import tools.refinery.store.tuple.Tuple; - -import java.util.function.Consumer; - -public interface ActionFactory { - Consumer prepare(Model model); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java new file mode 100644 index 00000000..ac9d125b --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/DanglingEdges.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +public enum DanglingEdges { + IGNORE, + DELETE, + FAIL +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java index f15c16e0..58b60499 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java @@ -16,7 +16,7 @@ public interface ModificationAdapter extends ModelAdapter { Tuple1 createObject(); - Tuple deleteObject(Tuple tuple); + boolean deleteObject(Tuple tuple, DanglingEdges danglingEdges); static ModificationBuilder builder() { return new ModificationBuilderImpl(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java new file mode 100644 index 00000000..5b86a5e1 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/CreateActionLiteral.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.actions; + +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +public class CreateActionLiteral extends AbstractActionLiteral { + private final NodeVariable variable; + + public CreateActionLiteral(NodeVariable variable) { + + this.variable = variable; + } + + public NodeVariable getVariable() { + return variable; + } + + @Override + public List getInputVariables() { + return List.of(); + } + + @Override + public List getOutputVariables() { + return List.of(variable); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var adapter = model.getAdapter(ModificationAdapter.class); + return ignoredTuple -> adapter.createObject(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java new file mode 100644 index 00000000..18ad2b9d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/DeleteActionLiteral.java @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.actions; + +import tools.refinery.store.dse.modification.DanglingEdges; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.dse.transition.actions.AbstractActionLiteral; +import tools.refinery.store.dse.transition.actions.BoundActionLiteral; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +public class DeleteActionLiteral extends AbstractActionLiteral { + private final NodeVariable variable; + private final DanglingEdges danglingEdges; + + public DeleteActionLiteral(NodeVariable variable, DanglingEdges danglingEdges) { + + this.variable = variable; + this.danglingEdges = danglingEdges; + } + + public NodeVariable getVariable() { + return variable; + } + + public DanglingEdges getDanglingEdges() { + return danglingEdges; + } + + @Override + public List getInputVariables() { + return List.of(variable); + } + + @Override + public List getOutputVariables() { + return List.of(); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var adapter = model.getAdapter(ModificationAdapter.class); + return tuple -> adapter.deleteObject(tuple, danglingEdges) ? Tuple.of() : null; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java new file mode 100644 index 00000000..31f50ac7 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/actions/ModificationActionLiterals.java @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.actions; + +import tools.refinery.store.dse.modification.DanglingEdges; +import tools.refinery.store.query.term.NodeVariable; + +public class ModificationActionLiterals { + private ModificationActionLiterals() { + throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly"); + } + + public static CreateActionLiteral create(NodeVariable variable) { + return new CreateActionLiteral(variable); + } + + public static DeleteActionLiteral delete(NodeVariable variable, DanglingEdges danglingEdges) { + return new DeleteActionLiteral(variable, danglingEdges); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java index b2a80d71..4e77c462 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java @@ -6,6 +6,7 @@ package tools.refinery.store.dse.modification.internal; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.modification.DanglingEdges; import tools.refinery.store.dse.modification.ModificationAdapter; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; @@ -13,6 +14,8 @@ import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; +import java.util.HashSet; + public class ModificationAdapterImpl implements ModificationAdapter { static final Symbol NEXT_ID = Symbol.of("NEXT_ID", 0, Integer.class, 0); @@ -49,14 +52,56 @@ public class ModificationAdapterImpl implements ModificationAdapter { } @Override - public Tuple deleteObject(Tuple tuple) { + public boolean deleteObject(Tuple tuple, DanglingEdges danglingEdges) { if (tuple.getSize() != 1) { throw new IllegalArgumentException("Tuple size must be 1"); } -// TODO: implement more efficient deletion - if (tuple.get(0) == getModelSize() - 1) { - nodeCountInterpretation.put(Tuple.of(), getModelSize() - 1); + int objectId = tuple.get(0); + if (danglingEdges == DanglingEdges.DELETE) { + deleteDanglingEdges(objectId); + } else if (danglingEdges == DanglingEdges.FAIL && hasDanglingEdges(objectId)) { + return false; + + } + int modelSize = getModelSize(); + if (objectId == modelSize - 1) { + nodeCountInterpretation.put(Tuple.of(), modelSize - 1); + } + return true; + } + + private void deleteDanglingEdges(int objectId) { + for (var symbol : model.getStore().getSymbols()) { + deleteDanglingEdges(objectId, (Symbol) symbol); + } + } + + private void deleteDanglingEdges(int objectId, Symbol symbol) { + var interpretation = model.getInterpretation(symbol); + var toDelete = new HashSet(); + int arity = symbol.arity(); + for (int i = 0; i < arity; i++) { + var cursor = interpretation.getAdjacent(i, objectId); + while (cursor.move()) { + toDelete.add(cursor.getKey()); + } + } + var defaultValue = symbol.defaultValue(); + for (var tuple : toDelete) { + interpretation.put(tuple, defaultValue); + } + } + + private boolean hasDanglingEdges(int objectId) { + for (var symbol : model.getStore().getSymbols()) { + var interpretation = model.getInterpretation(symbol); + int arity = symbol.arity(); + for (int i = 0; i < arity; i++) { + if (interpretation.getAdjacentSize(i, objectId) > 0) { + return true; + } + } } - return tuple; + return false; } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java index 3855a20a..800cf8f7 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java @@ -12,14 +12,16 @@ import tools.refinery.store.dse.transition.objectives.Objective; import java.util.Collection; import java.util.List; +// Builder pattern with methods returning {@code this} for convenience. +@SuppressWarnings("UnusedReturnValue") public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { + DesignSpaceExplorationBuilder transformation(Rule transformationRuleDefinition); - DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition); - default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRuleDefinitions) { + default DesignSpaceExplorationBuilder transformations(Rule... transformationRuleDefinitions) { return transformations(List.of(transformationRuleDefinitions)); } - default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { + default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { transformationRules.forEach(this::transformation); return this; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java index 5c8c7a4d..fb082fae 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java @@ -17,7 +17,7 @@ public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter @Override DesignSpaceExplorationAdapter createModelAdapter(Model model); - List getTransformations(); + List getTransformations(); List getAccepts(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java new file mode 100644 index 00000000..ff45ed3e --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Rule.java @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.dse.transition.actions.Action; +import tools.refinery.store.dse.transition.actions.BoundAction; +import tools.refinery.store.dse.transition.callback.*; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.dnf.RelationalQuery; + +public class Rule { + private final String name; + private final RelationalQuery precondition; + private final Action action; + + public Rule(String name, RelationalQuery precondition, Action action) { + if (precondition.arity() != action.getArity()) { + throw new IllegalArgumentException("Expected an action clause with %d parameters, got %d instead" + .formatted(precondition.arity(), action.getArity())); + } + this.name = name; + this.precondition = precondition; + this.action = action; + } + + public String getName() { + return name; + } + + public RelationalQuery getPrecondition() { + return precondition; + } + + public BoundAction createAction(Model model) { + return action.bindToModel(model); + } + + public static RuleBuilder builder(String name) { + return new RuleBuilder(name); + } + + public static RuleBuilder builder() { + return builder(null); + } + + public static Rule of(String name, RuleCallback0 callback) { + var builder = builder(name); + callback.accept(builder); + return builder.build(); + } + + public static Rule of(RuleCallback0 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback1 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1")); + return builder.build(); + } + + public static Rule of(RuleCallback1 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback2 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1"), builder.parameter("p2")); + return builder.build(); + } + + public static Rule of(RuleCallback2 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback3 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"), builder.parameter("p3")); + return builder.build(); + } + + public static Rule of(RuleCallback3 callback) { + return of(null, callback); + } + + public static Rule of(String name, RuleCallback4 callback) { + var builder = builder(name); + callback.accept(builder, builder.parameter("p1"), builder.parameter("p2"), builder.parameter("p3"), + builder.parameter("p4")); + return builder.build(); + } + + public static Rule of(RuleCallback4 callback) { + return of(null, callback); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java new file mode 100644 index 00000000..865ac369 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.dse.transition.actions.Action; +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.dse.transition.callback.*; +import tools.refinery.store.query.dnf.AbstractQueryBuilder; +import tools.refinery.store.query.dnf.Dnf; +import tools.refinery.store.query.term.Variable; + +import java.util.List; + +public class RuleBuilder extends AbstractQueryBuilder { + private final String name; + private List action; + + RuleBuilder(String name) { + super(Dnf.builder(name == null ? null : name + "#precondition")); + this.name = name; + } + + @Override + protected RuleBuilder self() { + return this; + } + + public RuleBuilder action(ActionLiteral... literals) { + return action(List.of(literals)); + } + + public RuleBuilder action(List literals) { + if (this.action != null) { + throw new IllegalStateException("Actions have already been set"); + } + this.action = List.copyOf(literals); + return this; + } + + public RuleBuilder action(Action action) { + return action(action.getActionLiterals()); + } + + public RuleBuilder action(ActionCallback0 callback) { + return action(callback.toLiterals()); + } + + public RuleBuilder action(ActionCallback1 callback) { + return action(callback.toLiterals(Variable.of("v1"))); + } + + public RuleBuilder action(ActionCallback2 callback) { + return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"))); + } + + public RuleBuilder action(ActionCallback3 callback) { + return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"), Variable.of("v3"))); + } + + public RuleBuilder action(ActionCallback4 callback) { + return action(callback.toLiterals(Variable.of("v1"), Variable.of("v2"), Variable.of("v3"), Variable.of("v4"))); + } + + public Rule build() { + var precondition = dnfBuilder.build().asRelation(); + return new Rule(name, precondition, Action.ofPrecondition(precondition, action)); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java index ab9fda3e..0eeccbdf 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java @@ -5,26 +5,27 @@ */ package tools.refinery.store.dse.transition; +import tools.refinery.store.dse.transition.actions.BoundAction; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.resultset.OrderedResultSet; import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.tuple.Tuple; -import java.util.function.Consumer; - public class Transformation { - private final TransformationRule definition; - + private final Rule definition; private final OrderedResultSet activations; + private final BoundAction action; - private final Consumer action; - - public Transformation(TransformationRule definition, OrderedResultSet activations, Consumer action) { + public Transformation(Model model, Rule definition) { this.definition = definition; - this.activations = activations; - this.action = action; + var precondition = definition.getPrecondition(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); + action = definition.createAction(model); } - public TransformationRule getDefinition() { + public Rule getDefinition() { return definition; } @@ -37,8 +38,6 @@ public class Transformation { } public boolean fireActivation(Tuple activation) { - action.accept(activation); - //queryEngine.flushChanges(); - return true; + return action.fire(activation); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java deleted file mode 100644 index d64a3db1..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.transition; - -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.ModelQueryBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.ActionFactory; -import tools.refinery.store.query.resultset.OrderedResultSet; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.tuple.Tuple; - -import java.util.*; - -public class TransformationRule { - - private final String name; - private final RelationalQuery precondition; - private final ActionFactory actionFactory; - - private Random random; - private ModelQueryAdapter queryEngine; - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { - this(name, precondition, actionFactory, new Random()); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { - this(name, precondition, actionFactory, new Random(seed)); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { - this.name = name; - this.precondition = precondition; - this.actionFactory = actionFactory; - this.random = random; - } - public void doConfigure(ModelStoreBuilder storeBuilder) { - var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); - queryBuilder.query(this.precondition); - } - - public Transformation prepare(Model model) { - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - var activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); - var action = actionFactory.prepare(model); - return new Transformation(this,activations,action); - } - - public String getName() { - return name; - } - - public RelationalQuery getPrecondition() { - return precondition; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java new file mode 100644 index 00000000..e30f06bb --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/AbstractActionLiteral.java @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +public abstract class AbstractActionLiteral implements ActionLiteral { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java new file mode 100644 index 00000000..d63ddfdd --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import org.eclipse.collections.api.factory.primitive.ObjectIntMaps; +import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dnf.SymbolicParameter; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.*; + +public class Action { + private final List parameters; + private final Set localVariables; + private final List actionLiterals; + private final int[] @Nullable [] inputAllocations; + private final int[] @Nullable [] outputAllocations; + + public Action(List parameters, List actionLiterals) { + this.parameters = List.copyOf(parameters); + this.actionLiterals = List.copyOf(actionLiterals); + var allocation = ObjectIntMaps.mutable.empty(); + int arity = parameters.size(); + for (int i = 0; i < arity; i++) { + allocation.put(parameters.get(i), i); + } + var mutableLocalVariables = new LinkedHashSet(); + int size = actionLiterals.size(); + inputAllocations = new int[size][]; + outputAllocations = new int[size][]; + for (int i = 0; i < size; i++) { + computeInputAllocation(i, parameters, allocation); + computeOutputAllocation(i, mutableLocalVariables, allocation); + } + this.localVariables = Collections.unmodifiableSet(mutableLocalVariables); + } + + private void computeInputAllocation(int actionIndex, List parameters, + MutableObjectIntMap allocation) { + var actionLiteral = actionLiterals.get(actionIndex); + var inputVariables = actionLiteral.getInputVariables(); + if (inputVariables.equals(parameters)) { + // Identity mappings use a {@code null} allocation to pass the activation tuple unchanged. + return; + } + var inputs = new int[inputVariables.size()]; + for (int i = 0; i < inputs.length; i++) { + var variable = inputVariables.get(i); + if (!allocation.containsKey(variable)) { + throw new IllegalArgumentException("Unbound input variable %s of action literal %s" + .formatted(variable, actionLiteral)); + } + inputs[i] = allocation.get(variable); + } + inputAllocations[actionIndex] = inputs; + } + + private void computeOutputAllocation(int actionIndex, Set mutableLocalVariable, + MutableObjectIntMap allocation) { + var actionLiteral = actionLiterals.get(actionIndex); + var outputVariables = actionLiteral.getOutputVariables(); + int size = outputVariables.size(); + if (size == 0) { + // Identity mappings use a {@code null} allocation to avoid iterating over the output tuple. + return; + } + if (size >= 2 && new HashSet<>(outputVariables).size() != size) { + throw new IllegalArgumentException("Action literal %s has duplicate output variables %s" + .formatted(actionLiteral, outputVariables)); + } + int arity = parameters.size(); + var outputs = new int[size]; + for (int i = 0; i < size; i++) { + var variable = outputVariables.get(i); + if (allocation.containsKey(variable)) { + throw new IllegalArgumentException("Output variable %s of action literal %s was already assigned" + .formatted(variable, actionLiteral)); + } + int variableId = mutableLocalVariable.size(); + allocation.put(variable, arity + variableId); + outputs[i] = variableId; + mutableLocalVariable.add(variable); + } + outputAllocations[actionIndex] = outputs; + } + + public List getParameters() { + return parameters; + } + + public int getArity() { + return parameters.size(); + } + + public Set getLocalVariables() { + return localVariables; + } + + public List getActionLiterals() { + return actionLiterals; + } + + int @Nullable [] getInputAllocation(int actionIndex) { + return inputAllocations[actionIndex]; + } + + int @Nullable [] getOutputAllocation(int actionIndex) { + return outputAllocations[actionIndex]; + } + + public BoundAction bindToModel(Model model) { + return new BoundAction(this, model); + } + + public static Action ofSymbolicParameters(List symbolicParameters, + List actionLiterals) { + var nodeVariables = symbolicParameters.stream() + .map(symbolicParameter -> symbolicParameter.getVariable().asNodeVariable()) + .toList(); + return new Action(nodeVariables, actionLiterals); + } + + public static Action ofPrecondition(RelationalQuery precondition, List actionLiterals) { + return ofSymbolicParameters(precondition.getDnf().getSymbolicParameters(), actionLiterals); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java new file mode 100644 index 00000000..a721ef73 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiteral.java @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +public interface ActionLiteral { + List getInputVariables(); + + List getOutputVariables(); + + BoundActionLiteral bindToModel(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java new file mode 100644 index 00000000..275e1e25 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/ActionLiterals.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.representation.Symbol; + +import java.util.List; +import java.util.Objects; + +public final class ActionLiterals { + private ActionLiterals() { + throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly"); + } + + public static PutActionLiteral put(Symbol symbol, T value, NodeVariable... parameters) { + return new PutActionLiteral<>(symbol, value, List.of(parameters)); + } + + public static PutActionLiteral add(Symbol symbol, NodeVariable... parameters) { + if (!Objects.equals(symbol.defaultValue(), false)) { + throw new IllegalArgumentException("Use put to add a value to symbols other than two-valued logic"); + } + return put(symbol, true, parameters); + } + + public static PutActionLiteral remove(Symbol symbol, NodeVariable... parameters) { + return put(symbol, symbol.defaultValue(), parameters); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java new file mode 100644 index 00000000..55f43735 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundAction.java @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.model.Model; +import tools.refinery.store.tuple.Tuple; + +public class BoundAction { + private final Action action; + private final BoundActionLiteral[] boundLiterals; + private Tuple activation; + private final int[] localVariables; + + BoundAction(Action action, Model model) { + this.action = action; + var actionLiterals = action.getActionLiterals(); + int size = actionLiterals.size(); + boundLiterals = new BoundActionLiteral[size]; + for (int i = 0; i < size; i++) { + boundLiterals[i] = actionLiterals.get(i).bindToModel(model); + } + localVariables = new int[action.getLocalVariables().size()]; + } + + public boolean fire(Tuple activation) { + if (this.activation != null) { + throw new IllegalStateException("Reentrant firing is not allowed"); + } + this.activation = activation; + try { + int size = boundLiterals.length; + for (int i = 0; i < size; i++) { + var inputAllocation = action.getInputAllocation(i); + var boundLiteral = boundLiterals[i]; + var input = getInputTuple(inputAllocation); + var output = boundLiteral.fire(input); + if (output == null) { + return false; + } + var outputAllocation = this.action.getOutputAllocation(i); + setOutputTuple(outputAllocation, output); + } + } finally { + this.activation = null; + } + return true; + } + + private Tuple getInputTuple(int @Nullable [] inputAllocation) { + if (inputAllocation == null) { + // Identity allocation. + return activation; + } + return switch (inputAllocation.length) { + case 0 -> Tuple.of(); + case 1 -> Tuple.of(getInput(inputAllocation[0])); + case 2 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1])); + case 3 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]), + getInput(inputAllocation[2])); + case 4 -> Tuple.of(getInput(inputAllocation[0]), getInput(inputAllocation[1]), + getInput(inputAllocation[2]), getInput(inputAllocation[3])); + default -> { + var elements = new int[inputAllocation.length]; + for (var i = 0; i < inputAllocation.length; i++) { + elements[i] = getInput(inputAllocation[i]); + } + yield Tuple.of(elements); + } + }; + } + + private int getInput(int index) { + int arity = action.getArity(); + return index < arity ? activation.get(index) : localVariables[index - arity]; + } + + private void setOutputTuple(int @Nullable [] outputAllocation, Tuple output) { + if (outputAllocation == null || outputAllocation.length == 0) { + return; + } + switch (outputAllocation.length) { + case 1 -> localVariables[outputAllocation[0]] = output.get(0); + case 2 -> { + localVariables[outputAllocation[0]] = output.get(0); + localVariables[outputAllocation[1]] = output.get(1); + } + case 3 -> { + localVariables[outputAllocation[0]] = output.get(0); + localVariables[outputAllocation[1]] = output.get(1); + localVariables[outputAllocation[2]] = output.get(2); + } + case 4 -> { + localVariables[outputAllocation[0]] = output.get(0); + localVariables[outputAllocation[1]] = output.get(1); + localVariables[outputAllocation[2]] = output.get(2); + localVariables[outputAllocation[3]] = output.get(3); + } + default -> { + for (int i = 0; i < outputAllocation.length; i++) { + localVariables[outputAllocation[i]] = output.get(i); + } + } + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java new file mode 100644 index 00000000..09c3c58c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/BoundActionLiteral.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import tools.refinery.store.tuple.Tuple; + +@FunctionalInterface +public interface BoundActionLiteral { + @Nullable + Tuple fire(@NotNull Tuple tuple); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java new file mode 100644 index 00000000..86288921 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/PutActionLiteral.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.actions; + +import tools.refinery.store.model.Model; +import tools.refinery.store.query.term.NodeVariable; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +public class PutActionLiteral extends AbstractActionLiteral { + private final Symbol symbol; + private final List parameters; + private final T value; + + public PutActionLiteral(Symbol symbol, T value, List parameters) { + if (symbol.arity() != parameters.size()) { + throw new IllegalArgumentException("Expected %d parameters for symbol %s, got %d instead" + .formatted(symbol.arity(), symbol, parameters.size())); + } + if (value != null && !symbol.valueType().isInstance(value)) { + throw new IllegalArgumentException("Expected value of type %s for symbol %s, got %s of type %s instead" + .formatted(symbol.valueType().getName(), symbol, value, value.getClass().getName())); + } + this.symbol = symbol; + this.parameters = List.copyOf(parameters); + this.value = value; + } + + public Symbol getSymbol() { + return symbol; + } + + public List getParameters() { + return parameters; + } + + public T getValue() { + return value; + } + + @Override + public List getInputVariables() { + return getParameters(); + } + + @Override + public List getOutputVariables() { + return List.of(); + } + + @Override + public BoundActionLiteral bindToModel(Model model) { + var interpretation = model.getInterpretation(symbol); + return tuple -> { + interpretation.put(tuple, value); + return Tuple.of(); + }; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java new file mode 100644 index 00000000..1190fdeb --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback0.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback0 { + List toLiterals(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java new file mode 100644 index 00000000..869f1a96 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback1.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback1 { + List toLiterals(NodeVariable v1); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java new file mode 100644 index 00000000..a648fc93 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback2.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback2 { + List toLiterals(NodeVariable v1, NodeVariable v2); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java new file mode 100644 index 00000000..a9b1d334 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback3.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback3 { + List toLiterals(NodeVariable v1, NodeVariable v2, NodeVariable v3); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java new file mode 100644 index 00000000..aef1351c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/ActionCallback4.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.actions.ActionLiteral; +import tools.refinery.store.query.term.NodeVariable; + +import java.util.List; + +@FunctionalInterface +public interface ActionCallback4 { + List toLiterals(NodeVariable v1, NodeVariable v2, NodeVariable v3, NodeVariable v4); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java new file mode 100644 index 00000000..538c23ba --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback0.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; + +@FunctionalInterface +public interface RuleCallback0 { + void accept(RuleBuilder builder); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java new file mode 100644 index 00000000..bd7bf4f5 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback1.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback1 { + void accept(RuleBuilder builder, NodeVariable p1); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java new file mode 100644 index 00000000..7b02b68a --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback2.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback2 { + void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java new file mode 100644 index 00000000..6f112d48 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback3.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback3 { + void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2, NodeVariable p3); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java new file mode 100644 index 00000000..dbcf8567 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/callback/RuleCallback4.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.callback; + +import tools.refinery.store.dse.transition.RuleBuilder; +import tools.refinery.store.query.term.NodeVariable; + +@FunctionalInterface +public interface RuleCallback4 { + void accept(RuleBuilder builder, NodeVariable p1, NodeVariable p2, NodeVariable p3, NodeVariable p4); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java index 4371cc03..a91f6870 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java @@ -7,13 +7,13 @@ package tools.refinery.store.dse.transition.internal; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; -import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.dse.transition.objectives.Criterion; import tools.refinery.store.dse.transition.objectives.Objective; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryBuilder; -import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -21,13 +21,13 @@ public class DesignSpaceExplorationBuilderImpl extends AbstractModelAdapterBuilder implements DesignSpaceExplorationBuilder { - LinkedHashSet transformationRuleDefinitions = new LinkedHashSet<>(); + LinkedHashSet transformationRuleDefinitions = new LinkedHashSet<>(); LinkedHashSet accepts = new LinkedHashSet<>(); LinkedHashSet excludes = new LinkedHashSet<>(); LinkedHashSet objectives = new LinkedHashSet<>(); @Override - public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition) { + public DesignSpaceExplorationBuilder transformation(Rule transformationRuleDefinition) { transformationRuleDefinitions.add(transformationRuleDefinition); return this; } @@ -53,23 +53,23 @@ public class DesignSpaceExplorationBuilderImpl @Override protected void doConfigure(ModelStoreBuilder storeBuilder) { - transformationRuleDefinitions.forEach(x -> x.doConfigure(storeBuilder)); - accepts.forEach(x -> x.doConfigure(storeBuilder)); - excludes.forEach(x -> x.doConfigure(storeBuilder)); - objectives.forEach(x -> x.doConfigure(storeBuilder)); + var queryEngine = storeBuilder.getAdapter(ModelQueryBuilder.class); + transformationRuleDefinitions.forEach(x -> queryEngine.query(x.getPrecondition())); + accepts.forEach(x -> x.configure(storeBuilder)); + excludes.forEach(x -> x.configure(storeBuilder)); + objectives.forEach(x -> x.configure(storeBuilder)); super.doConfigure(storeBuilder); } @Override protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { - List transformationRuleDefinitiions1 = new ArrayList<>(transformationRuleDefinitions); - List accepts1 = new ArrayList<>(accepts); - List excludes1 = new ArrayList<>(excludes); - List objectives1 = new ArrayList<>(objectives); + List transformationRuleDefinitionsList = List.copyOf(transformationRuleDefinitions); + List acceptsList = List.copyOf(accepts); + List excludesList = List.copyOf(excludes); + List objectivesList = List.copyOf(objectives); - return new DesignSpaceExplorationStoreAdapterImpl(store, - transformationRuleDefinitiions1, accepts1, - excludes1, objectives1); + return new DesignSpaceExplorationStoreAdapterImpl(store, transformationRuleDefinitionsList, acceptsList, + excludesList, objectivesList); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java index 3319e148..bd85e7a6 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -7,7 +7,7 @@ package tools.refinery.store.dse.transition.internal; import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; import tools.refinery.store.dse.transition.Transformation; -import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.Rule; import tools.refinery.store.dse.transition.objectives.Criterion; import tools.refinery.store.dse.transition.objectives.CriterionCalculator; import tools.refinery.store.dse.transition.objectives.Objective; @@ -20,18 +20,16 @@ import java.util.List; public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { protected final ModelStore store; - protected final List transformationRuleDefinitions; + protected final List ruleDefinitions; protected final List accepts; protected final List excludes; protected final List objectives; - public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, - List transformationRuleDefinitions, - List accepts, List excludes, - List objectives) { + public DesignSpaceExplorationStoreAdapterImpl( + ModelStore store, List ruleDefinitions, List accepts, List excludes, + List objectives) { this.store = store; - - this.transformationRuleDefinitions = transformationRuleDefinitions; + this.ruleDefinitions = ruleDefinitions; this.accepts = accepts; this.excludes = excludes; this.objectives = objectives; @@ -44,25 +42,31 @@ public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplor @Override public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { - final List t = this.transformationRuleDefinitions.stream().map(x->x.prepare(model)).toList(); - final List a = this.accepts.stream().map(x->x.createCalculator(model)).toList(); - final List e = this.excludes.stream().map(x->x.createCalculator(model)).toList(); - final List o = this.objectives.stream().map(x->x.createCalculator(model)).toList(); + final List t = this.ruleDefinitions.stream() + .map(x -> new Transformation(model, x)) + .toList(); + final List a = this.accepts.stream().map(x -> x.createCalculator(model)).toList(); + final List e = this.excludes.stream().map(x -> x.createCalculator(model)).toList(); + final List o = this.objectives.stream().map(x -> x.createCalculator(model)).toList(); return new DesignSpaceExplorationAdapterImpl(model, this, t, a, e, o); } + @Override - public List getTransformations() { - return transformationRuleDefinitions; + public List getTransformations() { + return ruleDefinitions; } + @Override public List getAccepts() { return accepts; } + @Override public List getExcludes() { return excludes; } + @Override public List getObjectives() { return objectives; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java index 66ca6f5e..4365cf9c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java @@ -9,7 +9,7 @@ import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStoreBuilder; public interface Criterion { - default void doConfigure(ModelStoreBuilder storeBuilder) { + default void configure(ModelStoreBuilder storeBuilder) { } CriterionCalculator createCalculator(Model model); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java index b5924455..d2476a2e 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java @@ -9,7 +9,7 @@ import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStoreBuilder; public interface Objective { - default void doConfigure(ModelStoreBuilder storeBuilder) { + default void configure(ModelStoreBuilder storeBuilder) { } ObjectiveCalculator createCalculator(Model model); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java index e2260cca..8d0a56cf 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java @@ -37,8 +37,8 @@ public class QueryCriteria implements Criterion { } @Override - public void doConfigure(ModelStoreBuilder storeBuilder) { - Criterion.super.doConfigure(storeBuilder); + public void configure(ModelStoreBuilder storeBuilder) { + Criterion.super.configure(storeBuilder); storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java index dfddccfc..9553e0e0 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java @@ -37,8 +37,8 @@ public class QueryObjective implements Objective { } @Override - public void doConfigure(ModelStoreBuilder storeBuilder) { - Objective.super.doConfigure(storeBuilder); + public void configure(ModelStoreBuilder storeBuilder) { + Objective.super.configure(storeBuilder); storeBuilder.getAdapter(ModelQueryBuilder.class).query(objectiveFunction); } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 36517709..0c50718e 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -5,13 +5,11 @@ */ package tools.refinery.store.dse; -import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; import java.util.List; @@ -107,19 +105,19 @@ class CRAExamplesTest { encapsulatesView.call(c1, f) )); - private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", - assignFeaturePrecondition, - (model) -> { -// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - var feature = activation.get(0); - var classElement = activation.get(1); - -// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); - encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); - }); - }); +// private static final Rule assignFeatureRule = new Rule("AssignFeature", +// assignFeaturePrecondition, +// (model) -> { +//// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); +// var encapsulatesInterpretation = model.getInterpretation(encapsulates); +// return ((Tuple activation) -> { +// var feature = activation.get(0); +// var classElement = activation.get(1); +// +//// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); +// encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); +// }); +// }); // private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", // deleteEmptyClassPrecondition, @@ -158,19 +156,19 @@ class CRAExamplesTest { // }); // }); - private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", - moveFeaturePrecondition, - (model) -> { - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - var classElement1 = activation.get(0); - var classElement2 = activation.get(1); - var feature = activation.get(2); - - encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); - encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); - }); - }); +// private static final Rule moveFeatureRule = new Rule("MoveFeature", +// moveFeaturePrecondition, +// (model) -> { +// var encapsulatesInterpretation = model.getInterpretation(encapsulates); +// return ((Tuple activation) -> { +// var classElement1 = activation.get(0); +// var classElement2 = activation.get(1); +// var feature = activation.get(2); +// +// encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); +// encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); +// }); +// }); // @Test // @Disabled("This test is only for debugging purposes") diff --git a/subprojects/store-reasoning/build.gradle.kts b/subprojects/store-reasoning/build.gradle.kts index 5885da83..ed8355f3 100644 --- a/subprojects/store-reasoning/build.gradle.kts +++ b/subprojects/store-reasoning/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } dependencies { - api(project(":refinery-store-query")) + api(project(":refinery-store-dse")) testImplementation(testFixtures(project(":refinery-store-query"))) testImplementation(project(":refinery-store-query-viatra")) } diff --git a/subprojects/visualization/build.gradle.kts b/subprojects/visualization/build.gradle.kts deleted file mode 100644 index abad0491..00000000 --- a/subprojects/visualization/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ - -plugins { - id("tools.refinery.gradle.java-library") -} - -dependencies { - api(project(":refinery-store-query")) -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java deleted file mode 100644 index ae87d8ac..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.map.Version; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; - -import java.util.Collection; - -public interface ModelVisualizerAdapter extends ModelAdapter { - - ModelVisualizerStoreAdapter getStoreAdapter(); - static ModelVisualizerBuilder builder() { - return new ModelVisualizerBuilderImpl(); - } - - public void addTransition(Version from, Version to, String action); - - - public void addTransition(Version from, Version to, String action, Tuple activation); - public void addState(Version state); - public void addState(Version state, Collection fitness); - public void addState(Version state, String label); - public void addSolution(Version state); - public void visualize(); - -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java deleted file mode 100644 index 592f5fcf..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization; - -import tools.refinery.store.adapter.ModelAdapterBuilder; -import tools.refinery.visualization.internal.FileFormat; - -public interface ModelVisualizerBuilder extends ModelAdapterBuilder { - ModelVisualizerBuilder withOutputpath(String outputpath); - ModelVisualizerBuilder withFormat(FileFormat format); - ModelVisualizerBuilder saveDesignSpace(); - ModelVisualizerBuilder saveStates(); -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java deleted file mode 100644 index 46663b2a..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization; - -import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.visualization.internal.FileFormat; - -import java.util.Set; - -public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { - - String getOutputPath(); - - boolean isRenderDesignSpace(); - - boolean isRenderStates(); - - Set getFormats(); -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java deleted file mode 100644 index c5dffeb2..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -public enum FileFormat { - BMP("bmp"), - DOT("dot"), - JPEG("jpg"), - PDF("pdf"), - PLAIN("plain"), - PNG("png"), - SVG("svg"); - - private final String format; - - FileFormat(String format) { - this.format = format; - } - - public String getFormat() { - return format; - } -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java deleted file mode 100644 index 04be22d6..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.visualization.ModelVisualizerStoreAdapter; - -import java.util.Set; - -public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter { - private final ModelStore store; - private final String outputPath; - private final boolean renderDesignSpace; - private final boolean renderStates; - private final Set formats; - - public ModelVisualizeStoreAdapterImpl(ModelStore store, String outputPath, Set formats, - boolean renderDesignSpace, boolean renderStates) { - this.store = store; - this.outputPath = outputPath; - this.formats = formats; - this.renderDesignSpace = renderDesignSpace; - this.renderStates = renderStates; - } - - @Override - public ModelStore getStore() { - return store; - } - - @Override - public ModelAdapter createModelAdapter(Model model) { - return new ModelVisualizerAdapterImpl(model, this); - } - - @Override - public String getOutputPath() { - return outputPath; - } - - @Override - public boolean isRenderDesignSpace() { - return renderDesignSpace; - } - - @Override - public boolean isRenderStates() { - return renderStates; - } - - @Override - public Set getFormats() { - return formats; - } -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java deleted file mode 100644 index 531969b4..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -import tools.refinery.store.map.Version; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.model.Model; -import tools.refinery.store.representation.AnySymbol; -import tools.refinery.store.representation.TruthValue; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.ModelVisualizerStoreAdapter; - -import java.io.*; -import java.util.*; -import java.util.stream.Collectors; - -public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { - private final Model model; - private final ModelVisualizerStoreAdapter storeAdapter; - private final Map> allInterpretations; - private final StringBuilder designSpaceBuilder = new StringBuilder(); - private final Map states = new HashMap<>(); - private int transitionCounter = 0; - private Integer numberOfStates = 0; - private final String outputPath; - private final Set formats; - private final boolean renderDesignSpace; - private final boolean renderStates; - - private static final Map truthValueToDot = Map.of( - TruthValue.TRUE, "1", - TruthValue.FALSE, "0", - TruthValue.UNKNOWN, "½", - TruthValue.ERROR, "E", - true, "1", - false, "0" - ); - - public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { - this.model = model; - this.storeAdapter = storeAdapter; - this.outputPath = storeAdapter.getOutputPath(); - this.formats = storeAdapter.getFormats(); - if (formats.isEmpty()) { - formats.add(FileFormat.SVG); - } - this.renderDesignSpace = storeAdapter.isRenderDesignSpace(); - this.renderStates = storeAdapter.isRenderStates(); - - this.allInterpretations = new HashMap<>(); - for (var symbol : storeAdapter.getStore().getSymbols()) { - var arity = symbol.arity(); - if (arity < 1 || arity > 2) { - continue; - } - var interpretation = (Interpretation) model.getInterpretation(symbol); - allInterpretations.put(symbol, interpretation); - } - designSpaceBuilder.append("digraph designSpace {\n"); - designSpaceBuilder.append(""" - nodesep=0 - ranksep=5 - node[ - \tstyle=filled - \tfillcolor=white - ] - """); - } - - @Override - public Model getModel() { - return model; - } - - @Override - public ModelVisualizerStoreAdapter getStoreAdapter() { - return storeAdapter; - } - - private String createDotForCurrentModelState() { - - var unaryTupleToInterpretationsMap = new HashMap>>(); - - var sb = new StringBuilder(); - - sb.append("digraph model {\n"); - sb.append(""" - node [ - \tstyle="filled, rounded" - \tshape=plain - \tpencolor="#00000088" - \tfontname="Helvetica" - ] - """); - sb.append(""" - edge [ - \tlabeldistance=3 - \tfontname="Helvetica" - ] - """); - - for (var entry : allInterpretations.entrySet()) { - var key = entry.getKey(); - var arity = key.arity(); - var cursor = entry.getValue().getAll(); - if (arity == 1) { - while (cursor.move()) { - unaryTupleToInterpretationsMap.computeIfAbsent(cursor.getKey(), k -> new LinkedHashSet<>()) - .add(entry.getValue()); - } - } else if (arity == 2) { - while (cursor.move()) { - var tuple = cursor.getKey(); - for (var i = 0; i < tuple.getSize(); i++) { - var id = tuple.get(i); - unaryTupleToInterpretationsMap.computeIfAbsent(Tuple.of(id), k -> new LinkedHashSet<>()); - } - sb.append(drawEdge(cursor.getKey(), key, entry.getValue())); - } - } - } - for (var entry : unaryTupleToInterpretationsMap.entrySet()) { - sb.append(drawElement(entry)); - } - sb.append("}"); - return sb.toString(); - } - - private StringBuilder drawElement(Map.Entry>> entry) { - var sb = new StringBuilder(); - - var tableStyle = " CELLSPACING=\"0\" BORDER=\"2\" CELLBORDER=\"0\" CELLPADDING=\"4\" STYLE=\"ROUNDED\""; - - var key = entry.getKey(); - var id = key.get(0); - var mainLabel = String.valueOf(id); - var interpretations = entry.getValue(); - var backgroundColor = toBackgroundColorString(averageColor(interpretations)); - - sb.append(id); - sb.append(" [\n"); - sb.append("\tfillcolor=\"").append(backgroundColor).append("\"\n"); - sb.append("\tlabel="); - if (interpretations.isEmpty()) { - sb.append("<\n\t").append(mainLabel).append(""); - } - else { - sb.append("<\n\t\t") - .append(mainLabel).append("\n"); - for (var interpretation : interpretations) { - var rawValue = interpretation.get(key); - - if (rawValue == null || rawValue.equals(TruthValue.FALSE) || rawValue.equals(false)) { - continue; - } - var color = "black"; - if (rawValue.equals(TruthValue.ERROR)) { - color = "red"; - } - var value = truthValueToDot.getOrDefault(rawValue, rawValue.toString()); - var symbol = interpretation.getSymbol(); - - if (symbol.valueType() == String.class) { - value = "\"" + value + "\""; - } - sb.append("\t\t") - .append(interpretation.getSymbol().name()) - .append("") - .append("=").append(value) - .append("\n"); - } - } - sb.append("\t\t>\n"); - sb.append("]\n"); - - return sb; - } - - private String drawEdge(Tuple edge, AnySymbol symbol, Interpretation interpretation) { - var value = interpretation.get(edge); - - if (value == null || value.equals(TruthValue.FALSE) || value.equals(false)) { - return ""; - } - - var sb = new StringBuilder(); - var style = "solid"; - var color = "black"; - if (value.equals(TruthValue.UNKNOWN)) { - style = "dotted"; - } - else if (value.equals(TruthValue.ERROR)) { - style = "dashed"; - color = "red"; - } - - var from = edge.get(0); - var to = edge.get(1); - var name = symbol.name(); - sb.append(from).append(" -> ").append(to) - .append(" [\n\tstyle=").append(style) - .append("\n\tcolor=").append(color) - .append("\n\tfontcolor=").append(color) - .append("\n\tlabel=\"").append(name) - .append("\"]\n"); - return sb.toString(); - } - - private String toBackgroundColorString(Integer[] backgroundColor) { - if (backgroundColor.length == 3) - return String.format("#%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2]); - else if (backgroundColor.length == 4) - return String.format("#%02x%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2], - backgroundColor[3]); - return null; - } - - private Integer[] typeColor(String name) { - var random = new Random(name.hashCode()); - return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 }; - } - - private Integer[] averageColor(Set> interpretations) { - if(interpretations.isEmpty()) { - return new Integer[]{256, 256, 256}; - } - // TODO: Only use interpretations where the value is not false (or unknown) - var symbols = interpretations.stream() - .map(i -> typeColor(i.getSymbol().name())).toArray(Integer[][]::new); - - - - return new Integer[] { - Arrays.stream(symbols).map(i -> i[0]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), - Arrays.stream(symbols).map(i -> i[1]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), - Arrays.stream(symbols).map(i -> i[2]).collect(Collectors.averagingInt(Integer::intValue)).intValue() - }; - } - - private String createDotForModelState(Version version) { - var currentVersion = model.getState(); - model.restore(version); - var graph = createDotForCurrentModelState(); - model.restore(currentVersion); - return graph; - } - - private boolean saveDot(String dot, String filePath) { - File file = new File(filePath); - file.getParentFile().mkdirs(); - - try (FileWriter writer = new FileWriter(file)) { - writer.write(dot); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - return true; - } - - private boolean renderDot(String dot, String filePath) { - return renderDot(dot, FileFormat.SVG, filePath); - } - - private boolean renderDot(String dot, FileFormat format, String filePath) { - try { - Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start(); - - OutputStream osToProcess = process.getOutputStream(); - PrintWriter pwToProcess = new PrintWriter(osToProcess); - pwToProcess.write(dot); - pwToProcess.close(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - public void addTransition(Version from, Version to, String action) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) - .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n"); - } - - @Override - public void addTransition(Version from, Version to, String action, Tuple activation) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) - .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / "); - - - for (int i = 0; i < activation.getSize(); i++) { - designSpaceBuilder.append(activation.get(i)); - if (i < activation.getSize() - 1) { - designSpaceBuilder.append(", "); - } - } - designSpaceBuilder.append("\"]\n"); - } - - @Override - public void addState(Version state) { - if (states.containsKey(state)) { - return; - } - states.put(state, numberOfStates++); - designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); - } - - @Override - public void addState(Version state, Collection fitness) { - var labelBuilder = new StringBuilder(); - for (var f : fitness) { - labelBuilder.append(f).append(", "); - } - addState(state, labelBuilder.toString()); - } - - @Override - public void addState(Version state, String label) { - if (states.containsKey(state)) { - return; - } - states.put(state, numberOfStates++); - designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); - designSpaceBuilder.append(label); - designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); - } - - @Override - public void addSolution(Version state) { - addState(state); - designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n"); - } - - private String buildDesignSpaceDot() { - designSpaceBuilder.append("}"); - return designSpaceBuilder.toString(); - } - - private boolean saveDesignSpace(String path) { - saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); - for (var entry : states.entrySet()) { - saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot"); - } - return true; - } - - private void renderDesignSpace(String path, Set formats) { - File filePath = new File(path); - filePath.mkdirs(); - if (renderStates) { - for (var entry : states.entrySet()) { - var stateId = entry.getValue(); - var stateDot = createDotForModelState(entry.getKey()); - for (var format : formats) { - if (format == FileFormat.DOT) { - saveDot(stateDot, path + "/" + stateId + ".dot"); - } - else { - renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); - } - } - } - } - if (renderDesignSpace) { - var designSpaceDot = buildDesignSpaceDot(); - for (var format : formats) { - if (format == FileFormat.DOT) { - saveDot(designSpaceDot, path + "/designSpace.dot"); - } - else { - renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); - } - } - } - } - - @Override - public void visualize() { - renderDesignSpace(outputPath, formats); - } -} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java deleted file mode 100644 index e4d801d8..00000000 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.visualization.internal; - -import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.visualization.ModelVisualizerBuilder; - -import java.util.LinkedHashSet; -import java.util.Set; - -public class ModelVisualizerBuilderImpl - extends AbstractModelAdapterBuilder - implements ModelVisualizerBuilder { - private String outputPath; - private boolean saveDesignSpace = false; - private boolean saveStates = false; - private Set formats = new LinkedHashSet<>(); - - @Override - protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { - return new ModelVisualizeStoreAdapterImpl(store, outputPath, formats, saveDesignSpace, saveStates); - } - - @Override - public ModelVisualizerBuilder withOutputpath(String outputpath) { - checkNotConfigured(); - this.outputPath = outputpath; - return this; - } - - @Override - public ModelVisualizerBuilder withFormat(FileFormat format) { - checkNotConfigured(); - this.formats.add(format); - return this; - } - - @Override - public ModelVisualizerBuilder saveDesignSpace() { - checkNotConfigured(); - this.saveDesignSpace = true; - return this; - } - - @Override - public ModelVisualizerBuilder saveStates() { - checkNotConfigured(); - this.saveStates = true; - return this; - } -} -- cgit v1.2.3-70-g09d2