From e2cbd86019c0273497fb54044a154ceb300e7b88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:49:37 +0000 Subject: [PATCH 1/4] Initial plan From cf5e21fa022d63b674a4e1860c3df447e0ff26df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:54:37 +0000 Subject: [PATCH 2/4] Add custom properties support to SolutionGeneratedEvent and ExcelSerializer Co-authored-by: rmartinsanta <55482385+rmartinsanta@users.noreply.github.com> --- .../events/types/SolutionGeneratedEvent.java | 29 ++++++++++++------ .../urjc/etsii/grafo/executors/Executor.java | 2 +- .../io/serializers/excel/ExcelSerializer.java | 30 +++++++++++++++---- .../grafo/testutil/TestHelperFactory.java | 8 ++++- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java b/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java index 5046bf705..5f724c034 100644 --- a/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java +++ b/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java @@ -34,20 +34,22 @@ public class SolutionGeneratedEvent, I extends Instance> private final long timeToBest; private final Algorithm algorithm; private final SoftReference solution; + private final Map 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 ot 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 algorithm, long executionTime, long timeToBest, MetricsStorage metrics, List timeStatsEvents) { + public SolutionGeneratedEvent(boolean success, String iteration, String instancePath, S solution, String experimentName, Algorithm algorithm, long executionTime, long timeToBest, Map solutionProperties, MetricsStorage metrics, List timeStatsEvents) { super(); this.success = success; this.iteration = iteration; @@ -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; @@ -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 getSolutionProperties() { + return solutionProperties; + } } diff --git a/core/src/main/java/es/urjc/etsii/grafo/executors/Executor.java b/core/src/main/java/es/urjc/etsii/grafo/executors/Executor.java index 0725a7546..bf92c7cad 100644 --- a/core/src/main/java/es/urjc/etsii/grafo/executors/Executor.java +++ b/core/src/main/java/es/urjc/etsii/grafo/executors/Executor.java @@ -180,7 +180,7 @@ protected void processWorkUnitResult(WorkUnitResult 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())); diff --git a/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java b/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java index da301c58c..62335e13d 100644 --- a/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java +++ b/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java @@ -125,9 +125,16 @@ public void _serializeResults(String experimentName, List 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 @@ -390,7 +397,13 @@ protected enum CType { public void fillRawSheet(SXSSFSheet rawSheet, boolean maximizing, List> results, List referenceResultProviders) { // Best values per instance Map 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 customPropertyKeysSet = new LinkedHashSet<>(); + for (var result : results) { + customPropertyKeysSet.addAll(result.getSolutionProperties().keySet()); + } + String[] customProperties = customPropertyKeysSet.toArray(new String[0]); // Create headers String[] commonHeaders = getCommonHeaders(); @@ -429,6 +442,13 @@ public void fillRawSheet(SXSSFSheet rawSheet, boolean maximizing, List 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 solutionGenerated(String instanceName, String expName, String algName, int iter, double score, long time, long ttb, Map 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 solutionGenerated(String instanceName, String expName, String algName, int iter, double score, long time, long ttb, Map> properties){ From fe85ed8417c7586cca86dd8ba36a41e10f1d0655 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:01:17 +0000 Subject: [PATCH 3/4] Add test for custom properties in Excel serializer Co-authored-by: rmartinsanta <55482385+rmartinsanta@users.noreply.github.com> --- .../excel/ExcelSerializerTest.java | 99 ++++++++++--------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/core/src/test/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializerTest.java b/core/src/test/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializerTest.java index f951b6f63..d81ebf013 100644 --- a/core/src/test/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializerTest.java +++ b/core/src/test/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializerTest.java @@ -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; @@ -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 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 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"); + } + } } From 209690dd196dc3f61d6223e695f0c7e8883f9b07 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:05:48 +0000 Subject: [PATCH 4/4] Address code review feedback: fix typo and use null for missing properties Co-authored-by: rmartinsanta <55482385+rmartinsanta@users.noreply.github.com> --- .../urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java | 2 +- .../urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java b/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java index 5f724c034..26c988da5 100644 --- a/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java +++ b/core/src/main/java/es/urjc/etsii/grafo/events/types/SolutionGeneratedEvent.java @@ -44,7 +44,7 @@ public class SolutionGeneratedEvent, I extends Instance> * @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 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 diff --git a/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java b/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java index 62335e13d..1d0c2bf6a 100644 --- a/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java +++ b/core/src/main/java/es/urjc/etsii/grafo/io/serializers/excel/ExcelSerializer.java @@ -447,7 +447,7 @@ public void fillRawSheet(SXSSFSheet rawSheet, boolean maximizing, List