Tutorial4Visualisations.java 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Create some graphs/plots
  2. //
  3. // 1. Use log-file to create graph of fitness against time
  4. // 2. Use similarities to create a heat map of the different models
  5. // 3. Use multi-dimensional scaling to generate a 2D representation of models
  6. // - plot this with optional clustering
  7. //
  8. // Requires following files on classpath:
  9. // 1. commons-csv-1.9.0.jar - https://repo1.maven.org/maven2/org/apache/commons/commons-csv/1.9.0/commons-csv-1.9.0.jar
  10. // 2. commons-math3-3.6.1.jar - https://repo1.maven.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
  11. // 3. mdsj.jar - https://www.inf.uni-konstanz.de/exalgo/software/mdsj/mdsj.jar
  12. // 4. xchart-3.8.2.jar - https://repo1.maven.org/maven2/org/knowm/xchart/xchart/3.8.2/xchart-3.8.2.jar
  13. //
  14. // e.g. on Windows, call with:
  15. // > java -cp commons-csv-1.9.0.jar;commons-math3-3.6.1.jar;mdsj.jar;xchart-3.8.2.jar Tutorial4Visualisations.java log-dmts.csv dmts-models.dat
  16. import java.nio.file.Files;
  17. import java.nio.file.Path;
  18. import java.io.IOException;
  19. import java.io.Reader;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.Optional;
  23. import mdsj.MDSJ;
  24. import org.apache.commons.csv.CSVFormat;
  25. import org.apache.commons.csv.CSVRecord;
  26. import org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer;
  27. import org.apache.commons.math3.ml.clustering.Clusterable;
  28. import org.knowm.xchart.SwingWrapper;
  29. import org.knowm.xchart.HeatMapChart;
  30. import org.knowm.xchart.HeatMapChartBuilder;
  31. import org.knowm.xchart.HeatMapSeries;
  32. import org.knowm.xchart.XYChart;
  33. import org.knowm.xchart.XYChartBuilder;
  34. import org.knowm.xchart.XYSeries;
  35. import org.knowm.xchart.style.colors.XChartSeriesColors;
  36. import org.knowm.xchart.style.markers.SeriesMarkers;
  37. public class Tutorial4Visualisations {
  38. public static void main (String[] args) {
  39. if (args.length == 2) {
  40. var logFile = Path.of(args[0]);
  41. var similarityFile = Path.of(args[1]);
  42. fitnessGraph(logFile);
  43. heatmapGraph(similarityFile);
  44. modelSimilarityGraph(similarityFile, 3);
  45. } else {
  46. System.out.println("Call with: log-file-name.csv similarity-file-name.dat");
  47. }
  48. }
  49. static void fitnessGraph(Path filepath) {
  50. try (Reader in = Files.newBufferedReader (filepath)) {
  51. // read in data
  52. List<LogFileEntry> data = CSVFormat.RFC4180.parse(in).stream()
  53. .map(LogFileEntry::fromCSVRecord)
  54. .flatMap(Optional::stream)
  55. .toList();
  56. // plot chart
  57. XYChart chart = new XYChartBuilder()
  58. .width(800)
  59. .height(600)
  60. .title("Fitness Against Generation: " + filepath.getFileName())
  61. .xAxisTitle("Generation")
  62. .yAxisTitle("Fitness")
  63. .build();
  64. chart.getStyler().setXAxisTickMarkSpacingHint(100);
  65. var xAxis = data.stream()
  66. .map(LogFileEntry::generation)
  67. .toList();
  68. var fitnessOverall = data.stream()
  69. .map(LogFileEntry::fitnessOverall)
  70. .toList();
  71. var fitnessAccuracy = data.stream()
  72. .map(LogFileEntry::fitnessAccuracy)
  73. .toList();
  74. var fitnessResponseTime = data.stream()
  75. .map(LogFileEntry::fitnessResponseTime)
  76. .toList();
  77. var fitnessProgramSize = data.stream()
  78. .map(LogFileEntry::fitnessProgramSize)
  79. .toList();
  80. XYSeries series = chart.addSeries("Fitness", xAxis, fitnessOverall);
  81. series.setLineColor(XChartSeriesColors.BLACK);
  82. series.setMarker(SeriesMarkers.NONE);
  83. series = chart.addSeries("Accuracy", xAxis, fitnessAccuracy);
  84. series.setLineColor(XChartSeriesColors.BLUE);
  85. series.setMarker(SeriesMarkers.NONE);
  86. series = chart.addSeries("Response Time", xAxis, fitnessResponseTime);
  87. series.setLineColor(XChartSeriesColors.RED);
  88. series.setMarker(SeriesMarkers.NONE);
  89. series = chart.addSeries("Program Size", xAxis, fitnessProgramSize);
  90. series.setLineColor(XChartSeriesColors.GREEN);
  91. series.setMarker(SeriesMarkers.NONE);
  92. new SwingWrapper<XYChart>(chart).displayChart();
  93. } catch (IOException ioe) {
  94. System.out.println("Exception: " + ioe);
  95. System.exit(-1);
  96. }
  97. }
  98. static void heatmapGraph (Path filepath) {
  99. var data = new ModelSimilarities (filepath);
  100. // plot chart
  101. HeatMapChart chart = new HeatMapChartBuilder()
  102. .width(800)
  103. .height(600)
  104. .title("Model Similarity: " + filepath.getFileName())
  105. .xAxisTitle("Individual")
  106. .yAxisTitle("Individual")
  107. .build();
  108. HeatMapSeries series = chart.addSeries("Similarities", data.xData, data.yData, data.similarities);
  109. new SwingWrapper<HeatMapChart>(chart).displayChart();
  110. }
  111. static void modelSimilarityGraph (Path filepath, int numClusters) {
  112. var data = new ModelSimilarities (filepath);
  113. var model = new KMeansPlusPlusClusterer<ModelSimilarities.Coord>(numClusters);
  114. var clusters = model.cluster (data.multiDimensionalScaling ());
  115. XYChart chart = new XYChartBuilder()
  116. .width(800)
  117. .height(600)
  118. .title("Model Similarity Visualisation: " + filepath.getFileName())
  119. .xAxisTitle("x")
  120. .yAxisTitle("y")
  121. .build();
  122. chart.getStyler().setDefaultSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Scatter);
  123. chart.getStyler().setLegendVisible(false);
  124. chart.getStyler().setMarkerSize(16);
  125. for (int c = 0; c < clusters.size (); c += 1) {
  126. var cluster = clusters.get (c);
  127. var xData = new double[cluster.getPoints().size ()];
  128. var yData = new double[cluster.getPoints().size ()];
  129. for (int i = 0; i < cluster.getPoints().size (); i += 1) {
  130. xData[i] = cluster.getPoints().get(i).x ();
  131. yData[i] = cluster.getPoints().get(i).y ();
  132. }
  133. chart.addSeries("Model Coordinate: " + c, xData, yData);
  134. }
  135. new SwingWrapper<XYChart>(chart).displayChart();
  136. }
  137. }
  138. record LogFileEntry(
  139. int generation,
  140. double fitnessOverall,
  141. double accuracy,
  142. double fitnessAccuracy,
  143. double responseTime,
  144. double fitnessResponseTime,
  145. double programSize,
  146. double fitnessProgramSize,
  147. double phase
  148. ) {
  149. static Optional<LogFileEntry> fromCSVRecord (CSVRecord record) {
  150. if (record.size() == 9) { // ensure we have enough parts
  151. try {
  152. int generation = Integer.parseInt(record.get(0));
  153. double fitnessOverall = Double.parseDouble(record.get(1));
  154. double accuracy = Double.parseDouble(record.get(2));
  155. double fitnessAccuracy = Double.parseDouble(record.get(3));
  156. double responseTime = Double.parseDouble(record.get(4));
  157. double fitnessResponseTime = Double.parseDouble(record.get(5));
  158. double programSize = Double.parseDouble(record.get(6));
  159. double fitnessProgramSize = Double.parseDouble(record.get(7));
  160. double phase = Double.parseDouble(record.get(8));
  161. return Optional.of(new LogFileEntry(
  162. generation,
  163. fitnessOverall,
  164. accuracy,
  165. fitnessAccuracy,
  166. responseTime,
  167. fitnessResponseTime,
  168. programSize,
  169. fitnessProgramSize,
  170. phase
  171. ));
  172. } catch (NumberFormatException e) {
  173. System.out.println("Exception: " + e);
  174. }
  175. }
  176. return Optional.empty();
  177. }
  178. }
  179. class ModelSimilarities {
  180. int[] xData;
  181. int[] yData;
  182. int[][] similarities;
  183. double[][] dissimilarities;
  184. ModelSimilarities (Path filepath) {
  185. try (Reader in = Files.newBufferedReader (filepath)) {
  186. // read in data
  187. List<ModelSimilarityEntry> data = CSVFormat.Builder
  188. .create(CSVFormat.RFC4180)
  189. .setDelimiter(' ')
  190. .build()
  191. .parse(in).stream()
  192. .map(ModelSimilarityEntry::fromCSVRecord)
  193. .flatMap(Optional::stream)
  194. .toList();
  195. xData = data.stream()
  196. .map(ModelSimilarityEntry::modelX)
  197. .distinct()
  198. .sorted()
  199. .mapToInt(i->i)
  200. .toArray();
  201. yData = data.stream()
  202. .map(ModelSimilarityEntry::modelY)
  203. .distinct()
  204. .sorted()
  205. .mapToInt(i->i)
  206. .toArray();
  207. similarities = new int[xData.length][yData.length];
  208. dissimilarities = new double[xData.length][yData.length];
  209. for (var entry : data) {
  210. similarities[entry.modelX()][entry.modelY()] = (int)Math.round(100 * entry.similarity());
  211. dissimilarities[entry.modelX()][entry.modelY()] = 1.0 - entry.similarity();
  212. }
  213. } catch (IOException ioe) {
  214. System.out.println("Model Similarities exception: " + ioe);
  215. System.exit(-1);
  216. }
  217. }
  218. List<Coord> multiDimensionalScaling () {
  219. var coords = MDSJ.classicalScaling(dissimilarities);
  220. var result = new ArrayList<Coord> ();
  221. for (int i = 0; i < coords[0].length; i += 1) {
  222. result.add (new Coord(coords[0][i], coords[1][i], new double[]{coords[0][i], coords[1][i]}));
  223. }
  224. return result;
  225. }
  226. record Coord(
  227. double x,
  228. double y,
  229. double[] point
  230. ) implements Clusterable {
  231. public double[] getPoint () {
  232. return point;
  233. }
  234. }
  235. record ModelSimilarityEntry(
  236. int modelX,
  237. int modelY,
  238. double similarity
  239. ) {
  240. static Optional<ModelSimilarityEntry> fromCSVRecord (CSVRecord record) {
  241. if (record.size() == 3) { // ensure we have enough parts
  242. try {
  243. int modelX = Integer.parseInt(record.get(0));
  244. int modelY = Integer.parseInt(record.get(1));
  245. double similarity = Double.parseDouble(record.get(2));
  246. return Optional.of(new ModelSimilarityEntry(
  247. modelX,
  248. modelY,
  249. similarity
  250. ));
  251. } catch (NumberFormatException e) {
  252. System.out.println("Exception: " + e);
  253. }
  254. }
  255. return Optional.empty();
  256. }
  257. }
  258. }