Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "PatchCat"]
path = PatchCat
url = https://github.com/karineek/PatchCat
1 change: 1 addition & 0 deletions PatchCat
Submodule PatchCat added at 693e2e
19 changes: 11 additions & 8 deletions src/main/java/gin/test/UnitTestResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public class UnitTestResultSet implements Serializable {
private final List<UnitTestResult> results;

private final Patch patch;
private final boolean patchValid;
private final boolean compiledOK;
private final Boolean patchValid;
private final Boolean compiledOK;
private String compileError = "N/A";
private final List<Boolean> editsValid;
private final String patchedCode;
Expand All @@ -30,9 +30,9 @@ public class UnitTestResultSet implements Serializable {
* was the patch effectively a no-op? i.e. was there some difference between
* input and output source?
*/
private final boolean noOp;
private final Boolean noOp;

public UnitTestResultSet(Patch patch, String patchedCode, boolean patchValid, List<Boolean> editsValid, boolean compiledOK, String compileError, boolean noOp, List<UnitTestResult> results) {
public UnitTestResultSet(Patch patch, String patchedCode, Boolean patchValid, List<Boolean> editsValid, Boolean compiledOK, String compileError, Boolean noOp, List<UnitTestResult> results) {
this.patch = patch;
this.patchValid = patchValid;
this.editsValid = new ArrayList<>(editsValid);
Expand All @@ -51,31 +51,34 @@ public String getPatchedCode() {
return patchedCode;
}

public boolean getValidPatch() {
public Boolean getValidPatch() {
return patchValid;
}

public List<Boolean> getEditsValid() {
return editsValid;
}

public boolean getCleanCompile() {
public Boolean getCleanCompile() {
return compiledOK;
}

public String getCompileError() {
return compileError;
}

public boolean getNoOp() {
public Boolean getNoOp() {
return noOp;
}

public List<UnitTestResult> getResults() {
return results;
}

public boolean allTestsSuccessful() {
public Boolean allTestsSuccessful() {
if (results.isEmpty()) {
return null;
}
for (UnitTestResult testResult : results) {
if (!testResult.getPassed()) {
return false;
Expand Down
50 changes: 43 additions & 7 deletions src/main/java/gin/util/GP.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Class<? extends Edit>> editTypes;

Expand Down Expand Up @@ -139,16 +142,33 @@ protected void sampleMethodsHook() {
/*============== Helper methods ==============*/

protected void writeNewHeader() {
String[] entry = { "MethodName"
, "Iteration"
String[] entry;

if (Boolean.TRUE.equals(patchCat)) {
entry = new String[] { "MethodName"
, "Iteration"
, "EvaluationNumber"
, "Patch"
, "Cluster"
, "Action"
, "Compiled"
, "AllTestsPassed"
, "TotalExecutionTime(ms)"
, "Fitness"
, "FitnessImprovement"
};
};
} else {
entry = new String[] { "MethodName"
, "Iteration"
, "EvaluationNumber"
, "Patch"
, "Compiled"
, "AllTestsPassed"
, "TotalExecutionTime(ms)"
, "Fitness"
, "FitnessImprovement"
};
}
try {
outputFileWriter = new CSVWriter(new FileWriter(outputFile));
outputFileWriter.writeNext(entry);
Expand All @@ -159,15 +179,31 @@ 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)
, results.getPatch().toString()
, results.getCleanCompile() == null ? "null" : Boolean.toString(results.getCleanCompile())
, results.allTestsSuccessful() == null ? "null" : Boolean.toString(results.allTestsSuccessful())
, Float.toString(results.totalExecutionTime() / 1000000.0f)
, fitness == null ? "null" : Double.toString(fitness)
, Double.toString(improvement)
};
outputFileWriter.writeNext(entry);
}

protected void writePatchWithPatchCatInfo(int iteration, int evaluationNumber, UnitTestResultSet results, String methodName, Double fitness, double improvement, int cluster, String action) {
String[] entry = { methodName
, Integer.toString(iteration)
, Integer.toString(evaluationNumber)
, results.getPatch().toString()
, Boolean.toString(results.getCleanCompile())
, Boolean.toString(results.allTestsSuccessful())
, Integer.toString(cluster)
, action
, results.getCleanCompile() == null ? "null" : Boolean.toString(results.getCleanCompile())
, results.allTestsSuccessful() == null ? "null" : Boolean.toString(results.allTestsSuccessful())
, Float.toString(results.totalExecutionTime() / 1000000.0f)
, Double.toString(fitness)
, fitness == null ? "null" : Double.toString(fitness)
, Double.toString(improvement)
};
outputFileWriter.writeNext(entry);
Expand Down
149 changes: 133 additions & 16 deletions src/main/java/gin/util/LocalSearchSimple.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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.
Expand All @@ -41,6 +46,9 @@ public abstract class LocalSearchSimple extends GP {

private List<Class <? extends Edit>> NoneLLMedit = new ArrayList<>();

private Double best = null;
private Patch bestPatch = null;

public LocalSearchSimple(String[] args) {
super(args);
SetLLMedits();
Expand Down Expand Up @@ -90,6 +98,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<UnitTest> tests, Patch patch, int iteration, int cluster) {
// ACTION C - Throw away patch
if (action == "C") {
UnitTestResultSet results = new UnitTestResultSet(patch, "", null, new ArrayList<>(), null, "", null, new ArrayList<>());
super.writePatchWithPatchCatInfo(iteration, iteration, results, methodName, null, 0, cluster, "C");
}

// ACTION B - Proceed as normal
if (action == "B") {
// Calculate fitness
UnitTestResultSet results = testPatch(className, tests, patch, null);
double newFitness = fitness(results);
super.writePatchWithPatchCatInfo(iteration, iteration, results, methodName, newFitness, compareFitness(newFitness, best), cluster, "B");

// Check if better
if (compareFitness(newFitness, best) > 0) {
best = newFitness;
bestPatch = patch;
Logger.info("New best patch found: " + bestPatch.toString() + " with fitness: " + best);
}
}

// 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, "", null, new ArrayList<>(), null, "", null, new ArrayList<>());
super.writePatchWithPatchCatInfo(iteration, iteration, results, methodName, null, 0, cluster, "A");
bestPatch = patch;
}
}

/*============== Implementation of abstract methods ==============*/

/*====== Search ======*/
Expand All @@ -108,26 +174,77 @@ protected void search(TargetMethod method, Patch origPatch) {

// Calculate fitness and record result, including fitness improvement (currently 0)
double orig = fitness(results);
super.writePatch(-1, 0, results, methodName, orig, 0);

// Keep best
double best = orig;
Patch bestPatch = origPatch;
if (Boolean.TRUE.equals(patchCat)){
super.writePatchWithPatchCatInfo(-1, 0, results, methodName, orig, 0, -1, "Original");
} else { super.writePatch(-1, 0, results, methodName, orig, 0); }

// Set original as best for now
best = orig;
bestPatch = origPatch;

for (int i = 1; i < indNumber; i++) {

// 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;
boolean toTest = false;
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/PatchCat/src/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, i, cluster);

} 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;
Logger.info("New best patch found: " + bestPatch.toString() + " with fitness: " + best);
}
}
} catch (IOException | InterruptedException e) {
Logger.info("Running PatchCat Failed");
e.printStackTrace();
}
}
}
Expand Down
Loading