diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5e02b3f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "PatchCat"] + path = PatchCat + url = https://github.com/karineek/PatchCat diff --git a/PatchCat b/PatchCat new file mode 160000 index 0000000..693e2e9 --- /dev/null +++ b/PatchCat @@ -0,0 +1 @@ +Subproject commit 693e2e9b029a660d6fb4b267e994facd03ec9072 diff --git a/src/main/java/gin/util/GP.java b/src/main/java/gin/util/GP.java index 060fe87..54d9a3c 100644 --- a/src/main/java/gin/util/GP.java +++ b/src/main/java/gin/util/GP.java @@ -53,6 +53,9 @@ public abstract class GP extends Sampler { @Argument(alias = "pb", description = "Probability of combined") protected Double combinedProbablity = 0.5; + @Argument(alias = "pc", description = "Enable patchCat") + protected Boolean patchCat = false; + // Allowed edit types for sampling: parsed from editType protected List> editTypes; @@ -159,7 +162,7 @@ protected void writeNewHeader() { } } - protected void writePatch(int iteration, int evaluationNumber, UnitTestResultSet results, String methodName, double fitness, double improvement) { + protected void writePatch(int iteration, int evaluationNumber, UnitTestResultSet results, String methodName, Double fitness, double improvement) { String[] entry = { methodName , Integer.toString(iteration) , Integer.toString(evaluationNumber) diff --git a/src/main/java/gin/util/LocalSearchSimple.java b/src/main/java/gin/util/LocalSearchSimple.java index aff100a..9929085 100644 --- a/src/main/java/gin/util/LocalSearchSimple.java +++ b/src/main/java/gin/util/LocalSearchSimple.java @@ -8,6 +8,7 @@ import gin.edit.llm.LLMMaskedStatement; import gin.edit.llm.LLMReplaceStatement; import gin.test.UnitTest; +import gin.test.UnitTestResult; import gin.test.UnitTestResultSet; import org.pmw.tinylog.Logger; @@ -21,7 +22,11 @@ import java.util.List; import java.util.Map; import java.util.Random; - +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Method-based LocalSearchSimple search. @@ -90,6 +95,64 @@ private void SetLLMedits () { @Override protected abstract boolean fitnessThreshold(UnitTestResultSet results, double orig); + public String clusterAction(int cluster) throws IOException{ + switch (cluster) { + case 4: + case 10: + case 15: + case 17: + return "A"; + case 0: + case 3: + case 5: + case 7: + case 8: + case 9: + case 11: + case 12: + case 13: + case 14: + case 16: + return "B"; + case 1: + case 2: + case 6: + return "C"; + } + throw new IOException("Clustering Failed"); + } + + public void implementClusterAction(String action, String className, String methodName, List tests, Patch patch, Patch bestPatch, Double orig, Double best, int iteration) { + // ACTION C - Throw away patch + if (action == "C") { + return; + } + + // ACTION B - Proceed as normal + if (action == "B") { + + //Calculate fitness + UnitTestResultSet results = testPatch(className, tests, patch, null); + double newFitness = fitness(results); + super.writePatch(iteration, iteration, results, methodName, newFitness, compareFitness(newFitness, orig)); + + // Check if better + if (compareFitness(newFitness, best) > 0) { + best = newFitness; + bestPatch = patch; + } + return; + } + + // ACTION A- Skip testing and keep patch- we need to figure out how to do this + else { + //Add dummy fitness entry as we don't want to test the patch + UnitTestResultSet results = new UnitTestResultSet(patch, "", true, new ArrayList(), true, "", true, new ArrayList()); //CH: two empty strings here OK? + super.writePatch(iteration, iteration, results, methodName, null, 0); //CH: is iteration for evaluationNumber here correct? + bestPatch = patch; + } + } + /*============== Implementation of abstract methods ==============*/ /*====== Search ======*/ @@ -118,16 +181,62 @@ protected void search(TargetMethod method, Patch origPatch) { // Add a mutation Patch patch = neighbour(bestPatch); - - // Calculate fitness - results = testPatch(className, tests, patch, null); - double newFitness = fitness(results); - super.writePatch(i, i, results, methodName, newFitness, compareFitness(newFitness, orig)); - - // Check if better - if (compareFitness(newFitness, best) > 0) { - best = newFitness; - bestPatch = patch; + Logger.info("Patch is: " + patch.toString()); + Logger.info("Original Patch is: " + origPatch.toString()); + + try { + // Support for PatchCat Integration + if (Boolean.TRUE.equals(patchCat)) { + Logger.info("Running PatchCat"); + + ProcessBuilder builder = new ProcessBuilder( + "python3", + "../gin-llm/clustering/PatchCat/PatchCat.py", + patch.toString(), origPatch.toString() + ); + + builder.environment().put("PYTHONUNBUFFERED", "1"); + + Process process = builder.start(); + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + String line = reader.readLine(); + int cluster = -1; + + Logger.info("Line is: " + line); + if (line != null && !line.isEmpty()) { + // Regex to capture digits inside [..], e.g. [13] + Pattern pattern = Pattern.compile("\\[(\\d+)]"); + Matcher matcher = pattern.matcher(line); + + if (matcher.find()) { + String numStr = matcher.group(1); // "13" + cluster = Integer.parseInt(numStr); + Logger.info("Cluster is: " + cluster); + } + } + + String action = clusterAction(cluster); + + int exit = process.waitFor(); + + implementClusterAction(action, className, methodName, tests, patch, bestPatch, orig, best, i); + } else { // Regular Local Search without PatchCat + // Calculate fitness + results = testPatch(className, tests, patch, null); + double newFitness = fitness(results); + super.writePatch(i, i, results, methodName, newFitness, compareFitness(newFitness, orig)); + + // Check if better + if (compareFitness(newFitness, best) > 0) { + best = newFitness; + bestPatch = patch; + } + } + } catch (IOException | InterruptedException e) { + Logger.info("Running PatchCat Failed"); + e.printStackTrace(); } } }