From 00c8fcf947d3f5c375d49a907bcefa5d8e1dd8ea Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 1 Aug 2023 12:41:45 +0200 Subject: Update visualization - Replace guru.nidi:graphviz-java with DOT based solution - Draws the design space as well as the model states - Design space nodes link to visualization of the sate --- subprojects/store-query-viatra/build.gradle.kts | 1 - .../DesignSpaceExplorationAdapterImpl.java | 21 ++++ .../tools/refinery/store/query/dse/DebugTest.java | 15 +-- subprojects/visualization/build.gradle.kts | 1 - .../visualization/ModelVisualizerAdapter.java | 23 ++-- .../internal/ModelVisualizerAdapterImpl.java | 120 +++++++++++---------- 6 files changed, 109 insertions(+), 72 deletions(-) diff --git a/subprojects/store-query-viatra/build.gradle.kts b/subprojects/store-query-viatra/build.gradle.kts index 1f804247..ef73e10a 100644 --- a/subprojects/store-query-viatra/build.gradle.kts +++ b/subprojects/store-query-viatra/build.gradle.kts @@ -10,7 +10,6 @@ plugins { dependencies { implementation(libs.ecore) - implementation ("guru.nidi:graphviz-java:0.18.1") api(libs.viatra) api(project(":refinery-store-query")) api(project(":refinery-store-reasoning")) diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java index c4c51e79..0fcc719d 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -15,6 +15,7 @@ import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; +import tools.refinery.visualization.ModelVisualizerAdapter; import java.util.*; @@ -40,6 +41,8 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private Random random = new Random(); private Objective[][] leveledObjectives; private boolean isNewState = false; + private final boolean isVisualizationEnabled; + private final ModelVisualizerAdapter modelVisualizerAdapter; public List getTrajectory() { return new LinkedList<>(trajectory); @@ -66,6 +69,8 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration statesAndUntraversedActivations = new HashMap<>(); statesAndTraversedActivations = new HashMap<>(); strategy = storeAdapter.getStrategy(); + modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); + isVisualizationEnabled = modelVisualizerAdapter != null; } @@ -129,6 +134,10 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration if (trajectory.size() < 2) { return false; } + if (isVisualizationEnabled) { + modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), + trajectory.get(trajectory.size() - 2), "backtrack"); + } model.restore(trajectory.get(trajectory.size() - 2)); trajectory.remove(trajectory.size() - 1); return true; @@ -137,6 +146,10 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public void restoreTrajectory(List trajectory) { model.restore(trajectory.get(trajectory.size() - 1)); +// if (isVisualizationEnabled) { +// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), +// trajectory.get(trajectory.size() - 1), "restore"); +// } this.trajectory = trajectory; } @@ -173,6 +186,9 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration public void newSolution() { var state = model.getState(); solutions.add(state); + if (isVisualizationEnabled) { + modelVisualizerAdapter.addSolution(state); + } } @Override @@ -212,6 +228,11 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration isNewState = !statesAndUntraversedActivations.containsKey(newState); statesAndUntraversedActivations.put(newState, getAllActivations()); statesAndTraversedActivations.put(newState, new HashSet<>()); + if (isVisualizationEnabled) { + modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2), + trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(), + activation.activation()); + } return true; } diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java index 969afbcb..821be7e6 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java @@ -5,6 +5,7 @@ import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.dse.strategy.BestFirstStrategy; import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; @@ -12,6 +13,7 @@ import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; public class DebugTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -83,30 +85,31 @@ public class DebugTest { .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() // .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() // .goOnOnlyIfFitnessIsBetter() )) - .with(ModelVisualizerAdapter.builder()) .build(); var model = store.createEmptyModel(); var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// dseAdapter.setRandom(1); var queryEngine = model.getAdapter(ModelQueryAdapter.class); var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); + var classElementInterpretation = model.getInterpretation(classElement); + var modelElement = dseAdapter.createObject(); + modelElementInterpretation.put(modelElement, true); + classElementInterpretation.put(modelElement, true); queryEngine.flushChanges(); var states = dseAdapter.explore(); var visualizer = model.getAdapter(ModelVisualizerAdapter.class); - for (var state : states) { - var visualization = visualizer.createVisualizationForModelState(state); - visualizer.saveVisualization(visualization, "test_output" + state.hashCode() + ".png"); - } + visualizer.renderDesignSpace("test_output", FileFormat.SVG); System.out.println("states size: " + states.size()); System.out.println("states: " + states); diff --git a/subprojects/visualization/build.gradle.kts b/subprojects/visualization/build.gradle.kts index 97b1688a..abad0491 100644 --- a/subprojects/visualization/build.gradle.kts +++ b/subprojects/visualization/build.gradle.kts @@ -9,6 +9,5 @@ plugins { } dependencies { - implementation ("guru.nidi:graphviz-java:0.18.1") 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 index 42f01242..2f71d4f3 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -1,8 +1,7 @@ package tools.refinery.visualization; -import guru.nidi.graphviz.engine.Format; -import guru.nidi.graphviz.model.MutableGraph; import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.internal.FileFormat; import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; @@ -13,21 +12,25 @@ public interface ModelVisualizerAdapter extends ModelAdapter { return new ModelVisualizerBuilderImpl(); } - public MutableGraph createVisualizationForCurrentModelState(); - - public MutableGraph createVisualizationForModelState(Long version); - public String createDotForCurrentModelState(); public String createDotForModelState(Long version); - public boolean saveVisualization(MutableGraph graph, String path); - - public boolean saveVisualization(MutableGraph graph, Format format, String path); - public boolean saveDot(String dot, String filePath); public boolean renderDot(String dot, String filePath); public boolean renderDot(String dot, FileFormat format, String filePath); + + public void addTransition(Long from, Long to, String action); + + + public void addTransition(Long from, Long to, String action, Tuple activation); + public void addSolution(Long state); + + public boolean saveDesignSpace(String path); + + public boolean renderDesignSpace(String path); + + public boolean renderDesignSpace(String path, FileFormat format); } 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 index 7456d913..9a284e24 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -1,14 +1,9 @@ package tools.refinery.visualization.internal; -import guru.nidi.graphviz.attribute.GraphAttr; -import guru.nidi.graphviz.attribute.Label; -import guru.nidi.graphviz.engine.Format; -import guru.nidi.graphviz.engine.Graphviz; -import guru.nidi.graphviz.model.MutableGraph; 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; @@ -16,12 +11,13 @@ import java.io.*; import java.util.HashMap; import java.util.Map; -import static guru.nidi.graphviz.model.Factory.*; - public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Model model; private final ModelVisualizerStoreAdapter storeAdapter; private final Map> interpretations; + private final StringBuilder designSpaceBuilder = new StringBuilder(); + private int transitionCounter = 0; + public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { this.model = model; this.storeAdapter = storeAdapter; @@ -46,6 +42,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { } interpretations.put(symbol, castInterpretation); } + designSpaceBuilder.append("digraph designSpace {\n"); } @Override @@ -58,36 +55,6 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return storeAdapter; } - @Override - public MutableGraph createVisualizationForCurrentModelState() { - var graph = mutGraph("model").setDirected(true).graphAttrs().add(GraphAttr.dpi(100)); - for (var entry : interpretations.entrySet()) { - var key = entry.getKey(); - var arity = key.arity(); - var cursor = entry.getValue().getAll(); - while (cursor.move()) { - if (arity == 1) { - var id = cursor.getKey().get(0); - graph.add(mutNode(String.valueOf(id)).add("label", key.name() + ": " + id)); - } else { - var from = cursor.getKey().get(0); - var to = cursor.getKey().get(1); - graph.add(mutNode(String.valueOf(from)).addLink(to(mutNode(String.valueOf(to))).with(Label.of(key.name())))); - } - } - } - return graph; - } - - @Override - public MutableGraph createVisualizationForModelState(Long version) { - var currentVersion = model.getState(); - model.restore(version); - var graph = createVisualizationForCurrentModelState(); - model.restore(currentVersion); - return graph; - } - @Override public String createDotForCurrentModelState() { var sb = new StringBuilder(); @@ -122,22 +89,6 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return graph; } - @Override - public boolean saveVisualization(MutableGraph graph, String path) { - return saveVisualization(graph, Format.PNG, path); - } - - @Override - public boolean saveVisualization(MutableGraph graph, Format format, String path) { - try { - Graphviz.fromGraph(graph).render(format).toFile(new File(path)); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - @Override public boolean saveDot(String dot, String filePath) { File file = new File(filePath); @@ -172,4 +123,65 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { } return true; } + + @Override + public void addTransition(Long from, Long to, String action) { + designSpaceBuilder.append(from).append(" -> ").append(to).append(" [label=\"").append(transitionCounter++) + .append(": ").append(action).append("\"]\n"); + + } + + @Override + public void addTransition(Long from, Long to, String action, Tuple activation) { + designSpaceBuilder.append(from).append(" -> ").append(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 addSolution(Long state) { + designSpaceBuilder.append(state).append(" [shape = doublecircle]\n"); + } + + private String buildDesignSpaceDot() { + for (var state : storeAdapter.getStore().getStates()) { + designSpaceBuilder.append(state).append(" [URL=\"./").append(state).append(".svg\"]\n"); + } + designSpaceBuilder.append("}"); + return designSpaceBuilder.toString(); + } + + @Override + public boolean saveDesignSpace(String path) { + saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); + for (var state : storeAdapter.getStore().getStates()) { + saveDot(createDotForModelState(state), path + "/" + state + ".dot"); + } + return true; + } + + @Override + public boolean renderDesignSpace(String path) { + return renderDesignSpace(path, FileFormat.SVG); + } + + @Override + public boolean renderDesignSpace(String path, FileFormat format) { + for (var state : storeAdapter.getStore().getStates()) { + var stateDot = createDotForModelState(state); + saveDot(stateDot, path + "/" + state + ".dot"); + renderDot(stateDot, path + "/" + state + "." + format.getFormat()); + } + var designSpaceDot = buildDesignSpaceDot(); + saveDot(designSpaceDot, path + "/designSpace.dot"); + return renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); + } } -- cgit v1.2.3-70-g09d2