Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,22 @@ public class SolutionGeneratedEvent<S extends Solution<S,I>, I extends Instance>
private final long timeToBest;
private final Algorithm<S,I> algorithm;
private final SoftReference<S> solution;
private final Map<String, Object> solutionProperties;

/**
* Create a new SolutionGeneratedEvent
*
* @param iteration solution iteration
* @param solution generated solution
* @param experimentName experiment name
* @param algorithm algorithm that generated this solution
* @param executionTime time used to generate this solution
* @param timeToBest time needed ot reach the best solution. timeToBest = totalTime - timeSinceLastModification
* @param metrics both framework calculated and user defined metrics
* @param timeStatsEvents
* @param iteration solution iteration
* @param solution generated solution
* @param experimentName experiment name
* @param algorithm algorithm that generated this solution
* @param executionTime time used to generate this solution
* @param timeToBest time needed to reach the best solution. timeToBest = totalTime - timeSinceLastModification
* @param solutionProperties custom properties computed for the solution
* @param metrics both framework calculated and user defined metrics
* @param timeStatsEvents time statistics events
*/
public SolutionGeneratedEvent(boolean success, String iteration, String instancePath, S solution, String experimentName, Algorithm<S, I> algorithm, long executionTime, long timeToBest, MetricsStorage metrics, List<TimeStatsEvent> timeStatsEvents) {
public SolutionGeneratedEvent(boolean success, String iteration, String instancePath, S solution, String experimentName, Algorithm<S, I> algorithm, long executionTime, long timeToBest, Map<String, Object> solutionProperties, MetricsStorage metrics, List<TimeStatsEvent> timeStatsEvents) {
super();
this.success = success;
this.iteration = iteration;
Expand All @@ -57,6 +59,7 @@ public SolutionGeneratedEvent(boolean success, String iteration, String instance
this.algorithm = algorithm;
this.executionTime = executionTime;
this.timeToBest = timeToBest;
this.solutionProperties = solutionProperties != null ? solutionProperties : Map.of();
this.algorithmName = algorithm.getName();
this.metrics = metrics;
this.timeStatsEvents = timeStatsEvents;
Expand Down Expand Up @@ -166,4 +169,12 @@ public MetricsStorage getMetrics() {
public boolean isSuccess() {
return success;
}

/**
* Get custom properties computed for this solution
* @return Map where key is property name, value is the computed property value
*/
public Map<String, Object> getSolutionProperties() {
return solutionProperties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ protected void processWorkUnitResult(WorkUnitResult<S, I> r, ProgressBar pb) {
io.exportSolution(r, SolutionExportFrequency.ALL);
}

var solutionGenerated = new SolutionGeneratedEvent<>(r.success(), r.iteration(), r.instancePath(), r.solution(), r.experimentName(), r.algorithm(), r.executionTime(), r.timeToTarget(), r.metrics(), r.timeData());
var solutionGenerated = new SolutionGeneratedEvent<>(r.success(), r.iteration(), r.instancePath(), r.solution(), r.experimentName(), r.algorithm(), r.executionTime(), r.timeToTarget(), r.solutionProperties(), r.metrics(), r.timeData());
EventPublisher.getInstance().publishEvent(solutionGenerated);
if (log.isDebugEnabled()) {
log.debug(String.format("\t%s.\tT(s): %.3f \tTTB(s): %.3f \t%s", r.iteration(), nanosToSecs(r.executionTime()), nanosToSecs(r.timeToTarget()), r.solution()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,16 @@ public void _serializeResults(String experimentName, List<SolutionGeneratedEvent
var pivotSheet = excelBook.createSheet(PIVOT_SHEET);
var otherDataSheet = excelBook.createSheet(OTHER_DATA_SHEET);

var areaString = String.format("%s1:%s%s", convertNumToColString(0), convertNumToColString(getCommonHeaders().length-1), 1_000_000);
// Extract custom properties from results
Set<String> customPropertyKeysSet = new LinkedHashSet<>();
for (var result : results) {
customPropertyKeysSet.addAll(result.getSolutionProperties().keySet());
}
String[] customProperties = customPropertyKeysSet.toArray(new String[0]);

var areaString = String.format("%s1:%s%s", convertNumToColString(0), convertNumToColString(getCommonHeaders().length + customProperties.length - 1), 1_000_000);
var area = new AreaReference(areaString, SpreadsheetVersion.EXCEL2007);
headRawSheet(rawSheet);
headRawSheet(rawSheet, customProperties);
fillPivotSheet(pivotSheet, area, rawSheet);

// Check and fill instance sheet if appropiate
Expand Down Expand Up @@ -390,7 +397,13 @@ protected enum CType {
public void fillRawSheet(SXSSFSheet rawSheet, boolean maximizing, List<? extends SolutionGeneratedEvent<?, ?>> results, List<ReferenceResultProvider> referenceResultProviders) {
// Best values per instance
Map<String, Double> bestValuesPerInstance = bestResultPerInstance(Context.getMainObjective(), results, referenceResultProviders, maximizing);
String[] customProperties = new String[0]; // TODO review and adapt impl

// Extract unique custom property keys from all results
Set<String> customPropertyKeysSet = new LinkedHashSet<>();
for (var result : results) {
customPropertyKeysSet.addAll(result.getSolutionProperties().keySet());
}
String[] customProperties = customPropertyKeysSet.toArray(new String[0]);

// Create headers
String[] commonHeaders = getCommonHeaders();
Expand Down Expand Up @@ -429,6 +442,13 @@ public void fillRawSheet(SXSSFSheet rawSheet, boolean maximizing, List<? extends
data[i][BEST_KNOWN_FOR_INSTANCE.getIndex()] = String.format("%s(%s, %s, %s)", function, refScoreCol, refInstanceNameCol, refCurrentInstance);
data[i][IS_BEST_KNOWN.getIndex()] = String.format("IF(%s=%s,1,0)", refBestKnownForInstance, refCurrentScore);
data[i][DEV_TO_BEST.getIndex()] = String.format("ABS(%s-%s)/%s", refCurrentScore, refBestKnownForInstance, refBestKnownForInstance);

// Fill custom properties
var solutionProps = r.getSolutionProperties();
for (int j = 0; j < customProperties.length; j++) {
String propName = customProperties[j];
data[i][commonHeaders.length + j] = solutionProps.getOrDefault(propName, null);
}
}

int currentRow = cutOff;
Expand Down Expand Up @@ -471,9 +491,7 @@ public void fillRawSheet(SXSSFSheet rawSheet, boolean maximizing, List<? extends
}
}

public void headRawSheet(SXSSFSheet rawSheet) {
String[] customProperties = new String[0];

public void headRawSheet(SXSSFSheet rawSheet, String[] customProperties) {
// Create headers
String[] commonHeaders = getCommonHeaders();
String[] headers = ArrayUtil.merge(commonHeaders, customProperties);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static es.urjc.etsii.grafo.testutil.TestHelperFactory.referencesGenerator;
Expand Down Expand Up @@ -192,49 +194,58 @@ void writeInstanceSheet(@TempDir Path temp) throws IOException {
}
}

// @Test
// void writeExcelWithCustomProperties(boolean useJavaCalculation, @TempDir Path temp) throws IOException {
// var instance = new TestInstance("writeInstanceSheetTest");
// instance.setProperty("customProperty", 1234567);
// var excel = initExcel(useJavaCalculation, Optional.empty(), temp, referencesGenerator(10.10, 10.10), TestHelperFactory.simpleInstanceManager(instance));
// var excelPath = temp.resolve("instanceSheet.xlsx");
// var data = solutionWithCustomPropertiesGenerator();
// excel.serializeResults("TestExperiment", data, excelPath);
// File excelFile = excelPath.toFile();
//
// try (var wb = new XSSFWorkbook(new FileInputStream(excelFile))) {
// XSSFSheet instanceSheet = wb.getSheet(ExcelSerializer.RAW_SHEET);
// Iterator<Row> rowIterator = instanceSheet.iterator();
//
// Assertions.assertTrue(rowIterator.hasNext(), "Missing headers in raw result sheet");
// var header = rowIterator.next();
// Assertions.assertEquals(11, header.getLastCellNum(), "Missing headers in instance sheet");
// // Get the index of "prop1"
// var prop1Index = -1;
// for (int i = 0; i < header.getLastCellNum(); i++) {
// if (header.getCell(i).getStringCellValue().equals("prop1")) {
// prop1Index = i;
// break;
// }
// }
// Assertions.assertTrue(prop1Index >= 0, "Missing prop1 header in raw result sheet");
// // Get the index of "prop2"
// var prop2Index = -1;
// for (int i = 0; i < header.getLastCellNum(); i++) {
// if (header.getCell(i).getStringCellValue().equals("prop2")) {
// prop2Index = i;
// break;
// }
// }
// Assertions.assertTrue(prop2Index >= 0, "Missing prop2 header in raw result sheet");
// Assertions.assertTrue(rowIterator.hasNext(), "Missing values in raw result sheet");
// var firstData = rowIterator.next();
// Assertions.assertEquals(4, firstData.getCell(prop1Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
// Assertions.assertEquals(1, firstData.getCell(prop2Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
// var secondData = rowIterator.next();
// Assertions.assertEquals(6, secondData.getCell(prop1Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
// Assertions.assertEquals(2, secondData.getCell(prop2Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
// }
// }
@Test
void writeExcelWithCustomProperties(@TempDir Path temp) throws IOException {
var instance = new TestInstance("writeCustomPropsTest");
var excel = initExcel(Optional.empty(), temp, referencesGenerator(10.10, 10.10), TestHelperFactory.simpleInstanceManager(instance));
var excelPath = temp.resolve("customProperties.xlsx");

// Create events with custom properties
var data = Arrays.asList(
TestHelperFactory.solutionGenerated("writeCustomPropsTest", "TestExp", "Alg1", 1, 100.0, 1000, 500, Map.of("prop1", 4, "prop2", 1)),
TestHelperFactory.solutionGenerated("writeCustomPropsTest", "TestExp", "Alg1", 2, 95.0, 1100, 600, Map.of("prop1", 6, "prop2", 2))
);

excel.serializeResults("TestExperiment", data, excelPath);
File excelFile = excelPath.toFile();

try (var wb = new XSSFWorkbook(new FileInputStream(excelFile))) {
XSSFSheet rawSheet = wb.getSheet(ExcelSerializer.RAW_SHEET);
Iterator<Row> rowIterator = rawSheet.iterator();

Assertions.assertTrue(rowIterator.hasNext(), "Missing headers in raw result sheet");
var header = rowIterator.next();
Assertions.assertEquals(11, header.getLastCellNum(), "Should have 9 common headers + 2 custom property headers");

// Find the index of "prop1"
var prop1Index = -1;
for (int i = 0; i < header.getLastCellNum(); i++) {
if (header.getCell(i) != null && "prop1".equals(header.getCell(i).getStringCellValue())) {
prop1Index = i;
break;
}
}
Assertions.assertTrue(prop1Index >= 0, "Missing prop1 header in raw result sheet");

// Find the index of "prop2"
var prop2Index = -1;
for (int i = 0; i < header.getLastCellNum(); i++) {
if (header.getCell(i) != null && "prop2".equals(header.getCell(i).getStringCellValue())) {
prop2Index = i;
break;
}
}
Assertions.assertTrue(prop2Index >= 0, "Missing prop2 header in raw result sheet");

Assertions.assertTrue(rowIterator.hasNext(), "Missing values in raw result sheet");
var firstData = rowIterator.next();
Assertions.assertEquals(4.0, firstData.getCell(prop1Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
Assertions.assertEquals(1.0, firstData.getCell(prop2Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");

var secondData = rowIterator.next();
Assertions.assertEquals(6.0, secondData.getCell(prop1Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
Assertions.assertEquals(2.0, secondData.getCell(prop2Index).getNumericCellValue(), "Data of solution custom properties is not correct in raw result sheet");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ public class TestHelperFactory {
public static SolutionGeneratedEvent<TestSolution, TestInstance> solutionGenerated(String instanceName, String expName, String algName, int iter, double score, long time, long ttb){
var solution = new TestSolution(new TestInstance(instanceName), score);
var algorithm = new TestAlgorithm(algName);
return new SolutionGeneratedEvent<>(true, String.valueOf(iter), instanceName, solution, expName, algorithm, time, ttb, new MetricsStorage(), new ArrayList<>());
return new SolutionGeneratedEvent<>(true, String.valueOf(iter), instanceName, solution, expName, algorithm, time, ttb, Map.of(), new MetricsStorage(), new ArrayList<>());
}

public static SolutionGeneratedEvent<TestSolution, TestInstance> solutionGenerated(String instanceName, String expName, String algName, int iter, double score, long time, long ttb, Map<String, Object> solutionProperties){
var solution = new TestSolution(new TestInstance(instanceName), score);
var algorithm = new TestAlgorithm(algName);
return new SolutionGeneratedEvent<>(true, String.valueOf(iter), instanceName, solution, expName, algorithm, time, ttb, solutionProperties, new MetricsStorage(), new ArrayList<>());
}

// public static SolutionGeneratedEvent<TestSolution, TestInstance> solutionGenerated(String instanceName, String expName, String algName, int iter, double score, long time, long ttb, Map<String, TreeSet<TimeValue>> properties){
Expand Down