diff options
author | 2017-07-12 15:40:33 +0200 | |
---|---|---|
committer | 2017-07-12 15:40:33 +0200 | |
commit | b4927437a3487ecc61c48d1351d18b7a38c5b154 (patch) | |
tree | 3f1e6c25c50a71efb0de6131b0d94c5163759dcc /Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz | |
parent | Rearranged the solver configuration into different subsections (diff) | |
download | VIATRA-Generator-b4927437a3487ecc61c48d1351d18b7a38c5b154.tar.gz VIATRA-Generator-b4927437a3487ecc61c48d1351d18b7a38c5b154.tar.zst VIATRA-Generator-b4927437a3487ecc61c48d1351d18b7a38c5b154.zip |
Graphviz based visualisation for the concretizations of partial models.
Diffstat (limited to 'Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz')
2 files changed, 263 insertions, 0 deletions
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz/PartialInterpretation2Graphviz.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz/PartialInterpretation2Graphviz.xtend new file mode 100644 index 00000000..a4baeadb --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz/PartialInterpretation2Graphviz.xtend | |||
@@ -0,0 +1,239 @@ | |||
1 | package hu.bme.mit.inf.dslreasoner.visualisation.pi2graphviz | ||
2 | |||
3 | import guru.nidi.graphviz.attribute.Arrow | ||
4 | import guru.nidi.graphviz.attribute.Color | ||
5 | import guru.nidi.graphviz.attribute.Shape | ||
6 | import guru.nidi.graphviz.attribute.Style | ||
7 | import guru.nidi.graphviz.engine.Format | ||
8 | import guru.nidi.graphviz.engine.Graphviz | ||
9 | import guru.nidi.graphviz.model.Graph | ||
10 | import guru.nidi.graphviz.model.Label | ||
11 | import guru.nidi.graphviz.model.Node | ||
12 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.DefinedElement | ||
13 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation | ||
14 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type | ||
15 | import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.TypeDefinition | ||
16 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.BinaryElementRelationLink | ||
17 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation | ||
18 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.visualisation.PartialInterpretationVisualisation | ||
19 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.visualisation.PartialInterpretationVisualiser | ||
20 | import hu.bme.mit.inf.dslreasoner.workspace.ReasonerWorkspace | ||
21 | import java.io.File | ||
22 | import java.util.HashMap | ||
23 | import java.util.HashSet | ||
24 | import java.util.LinkedList | ||
25 | import java.util.Random | ||
26 | import java.util.Set | ||
27 | |||
28 | import static guru.nidi.graphviz.model.Factory.* | ||
29 | import static guru.nidi.graphviz.attribute.Records.*; | ||
30 | |||
31 | import static extension hu.bme.mit.inf.dslreasoner.util.CollectionsUtil.* | ||
32 | import guru.nidi.graphviz.engine.Engine | ||
33 | import guru.nidi.graphviz.attribute.Records | ||
34 | import guru.nidi.graphviz.attribute.Attributes | ||
35 | import java.util.List | ||
36 | |||
37 | class GraphvizVisualisation implements PartialInterpretationVisualiser { | ||
38 | |||
39 | val TypeColoringStyle typeColoringStyle = TypeColoringStyle::AVERAGE; | ||
40 | val smallFontSize=9 | ||
41 | val keywords = #{"class","reference","literal","enum"} | ||
42 | |||
43 | override visualiseConcretization(PartialInterpretation partialInterpretation) { | ||
44 | visualisePartialInterpretation(partialInterpretation,true) | ||
45 | } | ||
46 | |||
47 | override visualisePartialSolution(PartialInterpretation partialInterpretation) { | ||
48 | visualisePartialInterpretation(partialInterpretation,false) | ||
49 | } | ||
50 | |||
51 | def private visualisePartialInterpretation(PartialInterpretation partialInterpretation, boolean concretizationOnly) { | ||
52 | val problem = partialInterpretation.problem | ||
53 | |||
54 | // Elements of the partial solutions | ||
55 | val oldElements = problem.elements | ||
56 | val newElements = partialInterpretation.newElements | ||
57 | //val prototypeElements = #[partialInterpretation.openWorldElementPrototype] | ||
58 | val allElements = problem.elements + partialInterpretation.newElements | ||
59 | |||
60 | // Indexing types | ||
61 | val mustTypes = new HashMap<DefinedElement,Set<Type>> | ||
62 | val mayTypes = new HashMap<DefinedElement,Set<Type>> | ||
63 | for(element : allElements) { | ||
64 | mustTypes.put(element,new HashSet) | ||
65 | mayTypes.put(element,new HashSet) | ||
66 | } | ||
67 | for(typeDefinition: problem.types.filter(TypeDefinition)) { | ||
68 | for(element : typeDefinition.elements) { | ||
69 | mustTypes.get(element)+=typeDefinition | ||
70 | } | ||
71 | } | ||
72 | for(partialTypeInterpretations: partialInterpretation.partialtypeinterpratation) { | ||
73 | for(element : partialTypeInterpretations.elements) { | ||
74 | mustTypes.get(element)+=partialTypeInterpretations.interpretationOf | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // Indexing references | ||
79 | |||
80 | // Drawing the nodes | ||
81 | val elements2Node = new HashMap | ||
82 | val elements2ID = new HashMap | ||
83 | for(oldElemenetIndex : 0..<oldElements.size) { | ||
84 | val oldElement = oldElements.get(oldElemenetIndex) | ||
85 | val id = '''o «oldElemenetIndex»''' | ||
86 | val image = drawElement(oldElement,id,true,oldElement.lookup(mustTypes),emptySet) | ||
87 | elements2ID.put(oldElement, id) | ||
88 | elements2Node.put(oldElement, image) | ||
89 | } | ||
90 | for(newElementIndex: 0..<newElements.size) { | ||
91 | val newElement = newElements.get(newElementIndex) | ||
92 | val id = '''n «newElementIndex»''' | ||
93 | val image = drawElement(newElement,id,false,newElement.lookup(mustTypes),emptySet) | ||
94 | elements2ID.put(newElement,id) | ||
95 | elements2Node.put(newElement,image) | ||
96 | } | ||
97 | // Prototype elements | ||
98 | |||
99 | // Drawing the edges | ||
100 | val edges = new HashMap | ||
101 | for(element : elements2Node.values) { | ||
102 | edges.put(element,new LinkedList) | ||
103 | } | ||
104 | for(relationInterpretation : partialInterpretation.partialrelationinterpretation) { | ||
105 | val type = relationInterpretation.interpretationOf | ||
106 | val isContainment = problem.containmentHierarchies.exists[it.containmentRelations.contains(type)] | ||
107 | for(link : relationInterpretation.relationlinks.filter(BinaryElementRelationLink)) { | ||
108 | val sourceNode = link.param1.lookup(elements2Node) | ||
109 | val targetNode = link.param2.lookup(elements2Node) | ||
110 | |||
111 | val edge = drawEdge(link,type,targetNode,isContainment) | ||
112 | sourceNode.lookup(edges).add(edge) | ||
113 | } | ||
114 | } | ||
115 | |||
116 | val graph = graph("PartialInterpretation").directed.with( | ||
117 | edges.entrySet.map[entry | entry.key.link(entry.value)] | ||
118 | ) | ||
119 | |||
120 | return new GraphvisVisualisation(graph) | ||
121 | } | ||
122 | |||
123 | def protected drawElement(DefinedElement element, String ID, boolean old, Set<Type> mustTypes, Set<Type> mayTypes) { | ||
124 | var tableStyle = ''' CELLSPACING="0" BORDER="2" CELLBORDER="0" CELLPADDING="1" STYLE="ROUNDED"''' | ||
125 | if(typeColoringStyle==TypeColoringStyle::AVERAGE) { | ||
126 | tableStyle += ''' BGCOLOR="#«typePredicateColor(mustTypes).toBackgroundColorString»"''' | ||
127 | } | ||
128 | val mainLabel = if(element.name !== null) { | ||
129 | val parts = element.name.split("\\s+") | ||
130 | textWithSubSup(parts.getOrNull(0),parts.getOrNull(1),parts.getOrNull(2),null) | ||
131 | } else { | ||
132 | val parts = ID.split("\\s+") | ||
133 | textWithSubSup(parts.get(0),parts.get(1),parts.getOrNull(2),null) | ||
134 | } | ||
135 | val label = Label.html( | ||
136 | '''<TABLE«tableStyle»>'''+ | ||
137 | '''<TR><TD COLSPAN="2" BORDER="2" SIDES="B">«mainLabel»</TD></TR>'''+ | ||
138 | '''«FOR mustTypeName : mustTypes.map[it.name].sort»«typePredicateDescription(mustTypeName,true)»«ENDFOR»'''+ | ||
139 | '''«FOR mayTypeName : mayTypes.map[it.name].sort»«typePredicateDescription(mayTypeName,false)»«ENDFOR»'''+ | ||
140 | '''</TABLE>''') | ||
141 | |||
142 | val node = node(ID).with(label).with( | ||
143 | Shape.NONE, | ||
144 | Attributes.attr("margin","0") | ||
145 | ) | ||
146 | return node | ||
147 | } | ||
148 | |||
149 | protected def drawEdge(BinaryElementRelationLink link, Relation relation, Node to, boolean isContainemnt) { | ||
150 | val nameSegments = relation.name.split("\\s+") | ||
151 | var l = to(to).with(Label.html(textWithSubSup(nameSegments.getOrNull(0),nameSegments.getOrNull(1),nameSegments.getOrNull(2),null))) | ||
152 | if(isContainemnt) { | ||
153 | l = l.with(Style.BOLD) | ||
154 | } | ||
155 | return l | ||
156 | } | ||
157 | |||
158 | private def <T> getOrNull(List<T> list, int index) { | ||
159 | if(list.size>index) return list.get(index) | ||
160 | else return null | ||
161 | } | ||
162 | |||
163 | def private textWithSubSup(String text, String sub, String sup, String after) { | ||
164 | val actualText = text | ||
165 | val actualSub = if(sub===null) {" "} else {sub.checkAndHighlightKeyword} | ||
166 | val actualSup = if(sup==null) {" "} else {sup.checkAndHighlightKeyword} | ||
167 | val actualAfter = if(after === null) {""} else { | ||
168 | '''<TD ROWSPAN="2" ALIGN="LEFT">«after»</TD>''' | ||
169 | } | ||
170 | return | ||
171 | '''<FONT FACE="helvetica"><Table CELLSPACING="0" BORDER="0" CELLBORDER="0" CELLPADDING="0">'''+ | ||
172 | '''<TR><TD ROWSPAN="2" ALIGN="RIGHT">«actualText»</TD><TD ALIGN="LEFT" VALIGN="BOTTOM"><FONT POINT-SIZE="«smallFontSize»">«actualSup»</FONT></TD>«actualAfter»</TR>'''+ | ||
173 | '''<TR><TD ALIGN="LEFT" VALIGN="TOP"><FONT POINT-SIZE="«smallFontSize»">«actualSub»</FONT></TD></TR>'''+ | ||
174 | '''</Table></FONT>''' | ||
175 | } | ||
176 | def checkAndHighlightKeyword(String word) { | ||
177 | if(keywords.contains(word)) { | ||
178 | return '''<B>«word»</B>''' | ||
179 | } else { | ||
180 | word | ||
181 | } | ||
182 | } | ||
183 | def typePredicateDescription(String typeName, boolean must) | ||
184 | { | ||
185 | val value = if(must){'''1'''} else{'''½'''} | ||
186 | val backgroundColor = if(this.typeColoringStyle == TypeColoringStyle.FLAG) { | ||
187 | ''' BGCOLOR="#«typeName.typePredicateColor.toBackgroundColorString»"''' | ||
188 | } else { | ||
189 | "" | ||
190 | } | ||
191 | val typeNameSegments = typeName.split("\\s+") | ||
192 | return '''<TR><TD«backgroundColor»>«textWithSubSup(typeNameSegments.getOrNull(0),typeNameSegments.getOrNull(1),typeNameSegments.getOrNull(2),null)»</TD><TD«backgroundColor»> = «value» </TD></TR>''' | ||
193 | } | ||
194 | def toBackgroundColorString(List<Integer> backgroundColor) { | ||
195 | '''«Integer.toHexString(backgroundColor.get(0))»«Integer.toHexString(backgroundColor.get(1))»«Integer.toHexString(backgroundColor.get(2))»''' | ||
196 | } | ||
197 | protected def typePredicateColor(Set<Type> types) { types.averageColor } | ||
198 | protected def typePredicateColor(String name) { | ||
199 | val Random random = new Random(name.hashCode+1) | ||
200 | val rangePicker = [|random.nextInt(128)+128] | ||
201 | return #[rangePicker.apply(), rangePicker.apply(), rangePicker.apply()] | ||
202 | } | ||
203 | private def averageColor(Set<Type> types) { | ||
204 | if(types.empty) { | ||
205 | return #[256,256,256] | ||
206 | } else { | ||
207 | val typeColors = types.filter[!it.isIsAbstract].map[typePredicateColor(it.name)] | ||
208 | return #[ | ||
209 | typeColors.map[get(0)].average, | ||
210 | typeColors.map[get(1)].average, | ||
211 | typeColors.map[get(2)].average | ||
212 | ] | ||
213 | } | ||
214 | } | ||
215 | private def average(Iterable<Integer> doubles) { return doubles.reduce[p1, p2|p1+p2]/doubles.size } | ||
216 | |||
217 | |||
218 | |||
219 | |||
220 | } | ||
221 | |||
222 | enum TypeColoringStyle { | ||
223 | FLAG, AVERAGE | ||
224 | } | ||
225 | |||
226 | class GraphvisVisualisation implements PartialInterpretationVisualisation { | ||
227 | val private Graph graph | ||
228 | |||
229 | public new(Graph graph) { | ||
230 | this.graph = graph | ||
231 | } | ||
232 | |||
233 | override writeToFile(ReasonerWorkspace workspace, String name) { | ||
234 | val path = '''«workspace.workspaceURI.toFileString»/«name».png''' | ||
235 | Graphviz.fromGraph(graph)//.engine(Engine::NEATO) | ||
236 | .render(Format.PNG).toFile(new File(path)); | ||
237 | } | ||
238 | |||
239 | } | ||
diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz/Test.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz/Test.xtend new file mode 100644 index 00000000..75187c26 --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.visualisation/src/hu/bme/mit/inf/dslreasoner/visualisation/pi2graphviz/Test.xtend | |||
@@ -0,0 +1,24 @@ | |||
1 | package hu.bme.mit.inf.dslreasoner.visualisation.pi2graphviz | ||
2 | |||
3 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation | ||
4 | import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialinterpretationPackage | ||
5 | import hu.bme.mit.inf.dslreasoner.workspace.FileSystemWorkspace | ||
6 | import org.eclipse.emf.ecore.resource.Resource | ||
7 | import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl | ||
8 | |||
9 | class Test { | ||
10 | def static void main(String[] args) { | ||
11 | val workspace = new FileSystemWorkspace("input/","") | ||
12 | |||
13 | PartialinterpretationPackage.eINSTANCE.eClass | ||
14 | Resource.Factory.Registry.INSTANCE.extensionToFactoryMap.put("*",new XMIResourceFactoryImpl) | ||
15 | |||
16 | val model = workspace.readModel(PartialInterpretation,"solution1.partialinterpretation") | ||
17 | println("loaded") | ||
18 | val translator = new GraphvizVisualisation | ||
19 | val visualisation = translator.visualiseConcretization(model) | ||
20 | println("visualised") | ||
21 | visualisation.writeToFile(workspace,"output") | ||
22 | println("saved") | ||
23 | } | ||
24 | } \ No newline at end of file | ||