From 45c7fb49a989a0f925697a0b9e3aab159dbc9a4e Mon Sep 17 00:00:00 2001 From: carolhanna01 Date: Tue, 6 Jan 2026 15:46:32 +0200 Subject: [PATCH 1/4] Adding PatchCat submodule --- .gitmodules | 3 +++ PatchCat | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 PatchCat 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 From fe38398d40d1cc2531f59b4643a2b9f5799fef3f Mon Sep 17 00:00:00 2001 From: carolhanna01 Date: Wed, 7 Jan 2026 00:14:04 +0200 Subject: [PATCH 2/4] Adding support for using PatchCat in Gin Local Search --- src/main/java/gin/util/LocalSearchSimple.java | 118 ++++++++++++++++-- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/src/main/java/gin/util/LocalSearchSimple.java b/src/main/java/gin/util/LocalSearchSimple.java index aff100a..68b66b9 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,63 @@ 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) { + // 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); + double newFitness = fitness(results); + super.writePatch(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()); + super.writePatch(results, methodName, 0, 0); + } + } + /*============== Implementation of abstract methods ==============*/ /*====== Search ======*/ @@ -119,15 +181,53 @@ 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)); + // Support for PatchCat Integration + + try { + System.out.println("Patch is: " + patch.toString()); + System.out.println("Original Patch is: " + origPatch.toString()); + + ProcessBuilder builder = new ProcessBuilder( + "python3", + "../gin-llm/clustering/PatchCat/PatchCat.py", + patch.toString(), origPatch.toString() + ); + + builder.environment().put("PYTHONUNBUFFERED", "1"); + + // Merge stderr -> stdout and append to the same file + // builder.redirectErrorStream(true); + // builder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile)); + + Process process = builder.start(); + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + String line = reader.readLine(); + int cluster = -1; + + System.out.println("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); + System.out.println("Cluster is: " + cluster); + } + } - // Check if better - if (compareFitness(newFitness, best) > 0) { - best = newFitness; - bestPatch = patch; + String action = clusterAction(cluster); + + int exit = process.waitFor(); + + implementClusterAction(action, className, methodName, tests, patch, bestPatch, orig, best); + + } catch (IOException | InterruptedException e) { + Logger.info("Running PatchCat Failed"); + e.printStackTrace(); } } } From ece49da67a16f630faf12e505d7464668d2f2bc3 Mon Sep 17 00:00:00 2001 From: carolhanna01 Date: Wed, 7 Jan 2026 13:37:55 +0200 Subject: [PATCH 3/4] Fixing compatibility issues with updates from LLM branch --- src/main/java/gin/util/LocalSearchSimple.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/gin/util/LocalSearchSimple.java b/src/main/java/gin/util/LocalSearchSimple.java index 68b66b9..6403b13 100644 --- a/src/main/java/gin/util/LocalSearchSimple.java +++ b/src/main/java/gin/util/LocalSearchSimple.java @@ -122,7 +122,7 @@ public String clusterAction(int cluster) throws IOException{ throw new IOException("Clustering Failed"); } - public void implementClusterAction(String action, String className, String methodName, List tests, Patch patch, Patch bestPatch, Double orig, Double best) { + 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; @@ -132,9 +132,9 @@ public void implementClusterAction(String action, String className, String metho if (action == "B") { //Calculate fitness - UnitTestResultSet results = testPatch(className, tests, patch); + UnitTestResultSet results = testPatch(className, tests, patch, null); double newFitness = fitness(results); - super.writePatch(results, methodName, newFitness, compareFitness(newFitness, orig)); + super.writePatch(iteration, iteration, results, methodName, newFitness, compareFitness(newFitness, orig)); // Check if better if (compareFitness(newFitness, best) > 0) { @@ -143,12 +143,12 @@ public void implementClusterAction(String action, String className, String metho } 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()); - super.writePatch(results, methodName, 0, 0); + UnitTestResultSet results = new UnitTestResultSet(patch, "", true, new ArrayList(), true, "", true, new ArrayList()); + super.writePatch(iteration, iteration, results, methodName, 0, 0); //CH: is iteration for evaluationNumber here correct? } } @@ -223,7 +223,7 @@ protected void search(TargetMethod method, Patch origPatch) { int exit = process.waitFor(); - implementClusterAction(action, className, methodName, tests, patch, bestPatch, orig, best); + implementClusterAction(action, className, methodName, tests, patch, bestPatch, orig, best, i); } catch (IOException | InterruptedException e) { Logger.info("Running PatchCat Failed"); From 17fa994022128ec936716953874aabbf4f75ab71 Mon Sep 17 00:00:00 2001 From: carolhanna01 Date: Thu, 15 Jan 2026 13:59:01 +0000 Subject: [PATCH 4/4] Adressing PR code review comments --- src/main/java/gin/util/GP.java | 5 +- src/main/java/gin/util/LocalSearchSimple.java | 87 ++++++++++--------- 2 files changed, 52 insertions(+), 40 deletions(-) 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 6403b13..9929085 100644 --- a/src/main/java/gin/util/LocalSearchSimple.java +++ b/src/main/java/gin/util/LocalSearchSimple.java @@ -147,8 +147,9 @@ public void implementClusterAction(String action, String className, String metho // 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()); - super.writePatch(iteration, iteration, results, methodName, 0, 0); //CH: is iteration for evaluationNumber here correct? + 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; } } @@ -180,51 +181,59 @@ protected void search(TargetMethod method, Patch origPatch) { // Add a mutation Patch patch = neighbour(bestPatch); - - // Support for PatchCat Integration + Logger.info("Patch is: " + patch.toString()); + Logger.info("Original Patch is: " + origPatch.toString()); try { - System.out.println("Patch is: " + patch.toString()); - System.out.println("Original Patch is: " + origPatch.toString()); - - ProcessBuilder builder = new ProcessBuilder( - "python3", - "../gin-llm/clustering/PatchCat/PatchCat.py", - patch.toString(), origPatch.toString() - ); + // Support for PatchCat Integration + if (Boolean.TRUE.equals(patchCat)) { + Logger.info("Running PatchCat"); - builder.environment().put("PYTHONUNBUFFERED", "1"); - - // Merge stderr -> stdout and append to the same file - // builder.redirectErrorStream(true); - // builder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile)); - - Process process = builder.start(); - BufferedReader reader = new BufferedReader( - new InputStreamReader(process.getInputStream()) - ); - String line = reader.readLine(); - int cluster = -1; - - System.out.println("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); - System.out.println("Cluster is: " + cluster); + 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); + String action = clusterAction(cluster); - int exit = process.waitFor(); + int exit = process.waitFor(); - implementClusterAction(action, className, methodName, tests, patch, bestPatch, orig, best, i); + 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();