aboutsummaryrefslogtreecommitdiff
path: root/src/dk.biks.bachelorizer/dk/biks/bachelorizer/Combi.java
blob: da28d5c12d0c4f1a6bb3d5f87aa44bdbd949e780 (plain)
  1. // SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk>
  2. // SPDX-License-Identifier: GPL-3.0-or-later
  3. package dk.biks.bachelorizer;
  4. import java.util.ArrayList;
  5. import java.util.Collection;
  6. import java.util.Collections;
  7. import java.util.HashSet;
  8. import java.util.Iterator;
  9. import java.util.LinkedList;
  10. import java.util.List;
  11. import java.util.Set;
  12. import java.util.stream.Collectors;
  13. import com.example.portfolio3.AdjListGraph;
  14. import com.example.portfolio3.Graph;
  15. import com.example.portfolio3.GraphAlgorithms;
  16. import com.example.portfolio3.MatrixGraph;
  17. import com.example.portfolio3.Vertex;
  18. /// Combi - static sample dataset of course combinations
  19. ///
  20. /// Slurps and parses data from upstream-provided comma-separated file.
  21. ///
  22. /// @version 0.0.1
  23. /// @see <https://moodle.ruc.dk/mod/assign/view.php?id=523186>
  24. public final class Combi {
  25. /// data about combinations as a Graph
  26. private Graph g;
  27. /// Default constructor
  28. ///
  29. /// @param path path to data file
  30. private Combi(final String path) {
  31. // read into temporary graph to resolve vertice count
  32. //
  33. // use Adjacency List Representation:
  34. // * cheap to bootstrap (done once)
  35. // * simple to count vertices (done once): O(1)
  36. Graph _g = new AdjListGraph();
  37. GraphAlgorithms.readGraph(_g, path);
  38. Integer _vertice_count = _g.vertices().size();
  39. // read into final graph
  40. //
  41. // use Adjacency Matrix Representation:
  42. // * expensive to bootstrap (done once)
  43. // * simple to add edges but needs vertice count
  44. // * simple to get edges (done repeatedly): O(1)
  45. //
  46. // TODO: avoid reading and parsing file twice
  47. Graph g = new MatrixGraph(_vertice_count);
  48. GraphAlgorithms.readGraph(g, path);
  49. // ensure the graph is connected
  50. assertConnected(g);
  51. nonOverlapping(g);
  52. // GraphAlgorithms.printGraph(g);
  53. GraphAlgorithms.printGraph(moduleGroups(nonOverlapping(g), g));
  54. // release temporary variables for garbage collection
  55. _g = null;
  56. _vertice_count = null;
  57. }
  58. /// JVM entry point
  59. ///
  60. /// @param args command-line arguments
  61. public static void main(final String[] args) {
  62. // first argument, if provided, is the data file path;
  63. // else use upstream named file in current directory.
  64. String path = (args.length > 0)
  65. ? args[0]
  66. : "combi.txt";
  67. Combi combi = new Combi(path);
  68. }
  69. /// utility function to check that a graph is connected
  70. ///
  71. /// If check fails, throw an unchecked exception,
  72. /// since it occurs at runtime and is unrecoverable.
  73. ///
  74. /// Time complexity of the operation is O(n²)
  75. /// where n is the amount of vertices,
  76. /// since visitDepthFirst() recurses out-edges of all vertices.
  77. ///
  78. /// @param g Graph to inspect
  79. /// @throws IllegalArgumentException
  80. /// https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html
  81. public static void assertConnected(Graph g) {
  82. // collect all vertices in the graph
  83. Collection<Vertex> c = g.vertices();
  84. // pick a random vertice in the graph
  85. Vertex v = g.vertices().iterator().next();
  86. // collect the set of visitable vertices
  87. Set<Vertex> visited = GraphAlgorithms.visitDepthFirst(
  88. g, v);
  89. // throw exception if not all vertices were visited
  90. if (visited.size() != c.size()) {
  91. throw new IllegalArgumentException(
  92. "graph is not connected");
  93. }
  94. }
  95. /// groups of subject modules with no overlapping students
  96. ///
  97. /// @param g Graph to inspect
  98. /// @return list of non-overlapping sets
  99. public static ArrayList<Set<Vertex>> nonOverlapping(Graph g) {
  100. // get all subject modules
  101. //
  102. // TODO: optionally seed this, to enable ranking control
  103. List<Vertex> modules = new ArrayList<>(g.vertices());
  104. Collections.shuffle(modules);
  105. return nonOverlapping(g, modules);
  106. }
  107. /// groups of subject modules with no overlapping students
  108. ///
  109. /// @param g Graph to inspect
  110. /// @param vip Ordered list of subject modules to prioritize
  111. /// @return list of non-overlapping sets
  112. public static ArrayList<Set<Vertex>> nonOverlapping(Graph g, List<Vertex> vip) {
  113. ArrayList<Set<Vertex>> sets = new ArrayList<>();
  114. // track done subject modules as extendable set
  115. //
  116. // HashList used for efficient addition and lookup
  117. Set<Vertex> done = new HashSet<>();
  118. // convert module list to a set
  119. //
  120. // defined once outside loop as a slight optimization
  121. Set<Vertex> all_set = new HashSet<>(vip);
  122. // iterate over subject modules
  123. for (Vertex current : vip) {
  124. if (done.contains(current)) {
  125. continue;
  126. }
  127. // add set of current and any unconnected modules
  128. // TODO: try tighten to include current in loop
  129. Set<Vertex> isolated = all_set.stream()
  130. .filter(x ->
  131. {
  132. if (x == current)
  133. return false;
  134. Integer weight = g.getWeight(current, x);
  135. return weight == null
  136. && !done.contains(x);
  137. }).collect(Collectors.toSet());
  138. isolated.add(current);
  139. // add as set and omit from future iterations
  140. sets.add(isolated);
  141. done.addAll(isolated);
  142. }
  143. for(Set<Vertex> s: sets) {
  144. System.out.println("set: "+s.size());
  145. for (Vertex v: s) {
  146. System.out.println(" "+v.toString());
  147. }
  148. }
  149. return sets;
  150. }
  151. public static Graph moduleGroups(ArrayList<Set<Vertex>> sets, Graph g) {
  152. Graph h = new AdjListGraph();
  153. for (Set<Vertex> s: sets) {
  154. for (Set<Vertex> t: sets) {
  155. if (t == s) continue;
  156. int sum = 0;
  157. for (Vertex v: s) {
  158. // get num of students with this combination of modules.
  159. for (Vertex w: t) {
  160. Integer weight = g.getWeight(v, w);
  161. if (weight != null) {
  162. sum += weight;
  163. }
  164. }
  165. }
  166. h.insertEdge(s.toString(), t.toString(), sum);
  167. }
  168. }
  169. return h;
  170. }
  171. }