diff --git a/src/report/DataSyncReport.java b/src/report/DataSyncReport.java index 1fde09e235c5cd31357d5c38070d1d8490017744..573f23aa2b7dab651a046d364021d23515a29053 100644 --- a/src/report/DataSyncReport.java +++ b/src/report/DataSyncReport.java @@ -127,6 +127,7 @@ public class DataSyncReport extends SamplingReport{ //Write out statistics gathered over all hosts with database write("sim_time: " + format(getSimTime()) +", "+ "avg_used_mem: " + getAverage(usedDataBasePercentage) +"%, "+ + "min_used_mem: " + getMinimum(usedDataBasePercentage) +"%, "+ "max_used_mem: " + getMaximum(usedDataBasePercentage) +"%, "+ "med_avg_data_util: " + getMedian(averageDataUtility)+ ", "+ "avg_data_util: " + getAverage(averageDataUtility) + ", "+ diff --git a/src/report/Report.java b/src/report/Report.java index 1b9be30127baa6e46572ce35c341d9fe89fbe604..479e69888737a25cf797301b667ef0028886e107 100644 --- a/src/report/Report.java +++ b/src/report/Report.java @@ -414,6 +414,21 @@ public abstract class Report { return format(max); } + /** + * Returns the minimum of the values in the list + * as a formatted string with the given numerical precision. + * Return "NaN" for empty lists. + * + * @param values the list to search the minimum in + * @return a formatted string of the minimum in the list + */ + public String getMinimum(List<Double> values){ + if (values.isEmpty()){ + return NAN; + } + double min = Collections.min(values); + return format(min); + } /** * Returns the variance of the values in the List. * diff --git a/src/test/DataSyncReportTest.java b/src/test/DataSyncReportTest.java index b22a8df6fbac739724569938e1ebf375a7be0ad0..eda30cb19aa13b3ef7e58fa0900fd86a10108159 100644 --- a/src/test/DataSyncReportTest.java +++ b/src/test/DataSyncReportTest.java @@ -35,30 +35,30 @@ public class DataSyncReportTest extends AbstractReportTest{ private static final long SMALLEST_DB_SIZE = 200L; /* String arrays containing all the expected metrics and ratios to check whether everything necessary is contained*/ - private static final String[] EXPECTED_METRICS = new String[]{"avg_used_mem", "max_used_mem", "med_avg_data_util", - "avg_data_util", "med_avg_data_age", "avg_data_age", "med_max_data_age", "med_avg_data_dist", - "avg_data_dist", "med_max_data_dist"}; + private static final String[] EXPECTED_METRICS = new String[]{"avg_used_mem", "min_used_mem", "max_used_mem", + "med_avg_data_util", "avg_data_util", "med_avg_data_age", "avg_data_age", "med_max_data_age", + "med_avg_data_dist", "avg_data_dist", "med_max_data_dist"}; private static final String[] EXPECTED_RATIOS= new String[]{"avg_ratio_map", "avg_ratio_marker", "avg_ratio_skill", "avg_ratio_res"}; /* Expected lines */ private static final String EXPECTED_FIRST_LINE="Data sync stats for scenario TEST-Scenario"; private static final String[] EXPECTED_OUTPUTS= new String []{ - "sim_time: 51.0, avg_used_mem: 0.0%, max_used_mem: 0.0%, med_avg_data_util: NaN, avg_data_util: NaN, " + - "med_avg_data_age: NaN, avg_data_age: NaN, med_max_data_age: NaN, med_avg_data_dist: NaN, " + - "avg_data_dist: NaN, med_max_data_dist: NaN", + "sim_time: 51.0, avg_used_mem: 0.0%, min_used_mem: 0.0%, max_used_mem: 0.0%, med_avg_data_util: NaN, " + + "avg_data_util: NaN, med_avg_data_age: NaN, avg_data_age: NaN, med_max_data_age: NaN, " + + "med_avg_data_dist: NaN, avg_data_dist: NaN, med_max_data_dist: NaN", "avg_ratio_map: NaN%, avg_ratio_marker: NaN%, avg_ratio_skill: NaN%, avg_ratio_res: NaN%", - "sim_time: 81.0, avg_used_mem: 3.7%, max_used_mem: 7.3%, med_avg_data_util: 1.0, avg_data_util: 1.0, " + - "med_avg_data_age: NaN, avg_data_age: NaN, med_max_data_age: NaN, med_avg_data_dist: 0.0, " + - "avg_data_dist: 0.0, med_max_data_dist: 0.0", + "sim_time: 81.0, avg_used_mem: 3.7%, min_used_mem: 0.0%, max_used_mem: 7.3%, med_avg_data_util: 1.0, " + + "avg_data_util: 1.0, med_avg_data_age: NaN, avg_data_age: NaN, med_max_data_age: NaN, " + + "med_avg_data_dist: 0.0, avg_data_dist: 0.0, med_max_data_dist: 0.0", "avg_ratio_map: 100.0%, avg_ratio_marker: 0.0%, avg_ratio_skill: 0.0%, avg_ratio_res: 0.0%", - "sim_time: 111.0, avg_used_mem: 8.1%, max_used_mem: 8.8%, med_avg_data_util: 1.0, avg_data_util: 1.0, " + - "med_avg_data_age: 91.0, avg_data_age: 91.0, med_max_data_age: 91.0, med_avg_data_dist: 565.7, " + - "avg_data_dist: 282.8, med_max_data_dist: 565.7", + "sim_time: 111.0, avg_used_mem: 8.1%, min_used_mem: 7.3%, max_used_mem: 8.8%, med_avg_data_util: 1.0, " + + "avg_data_util: 1.0, med_avg_data_age: 91.0, avg_data_age: 91.0, med_max_data_age: 91.0, " + + "med_avg_data_dist: 565.7, avg_data_dist: 282.8, med_max_data_dist: 565.7", "avg_ratio_map: 50.0%, avg_ratio_marker: 0.0%, avg_ratio_skill: 50.0%, avg_ratio_res: 0.0%", - "sim_time: 141.0, avg_used_mem: 22.7%, max_used_mem: 30.8%, med_avg_data_util: 1.0, avg_data_util: 0.9, " + - "med_avg_data_age: 121.0, avg_data_age: 90.8, med_max_data_age: 121.0, med_avg_data_dist: 848.5, " + - "avg_data_dist: 565.7, med_max_data_dist: 1131.4", + "sim_time: 141.0, avg_used_mem: 22.7%, min_used_mem: 14.7%, max_used_mem: 30.8%, med_avg_data_util: 1.0, " + + "avg_data_util: 0.9, med_avg_data_age: 121.0, avg_data_age: 90.8, med_max_data_age: 121.0, " + + "med_avg_data_dist: 848.5, avg_data_dist: 565.7, med_max_data_dist: 1131.4", "avg_ratio_map: 25.0%, avg_ratio_marker: 25.0%, avg_ratio_skill: 50.0%, avg_ratio_res: 0.0%" }; diff --git a/src/test/ReportTest.java b/src/test/ReportTest.java index a041c5d8bd232c52acebceb19a9f8b54dde5bc22..05693714ce714d71cfd1df6e5fd9c769df4ef6b8 100644 --- a/src/test/ReportTest.java +++ b/src/test/ReportTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.*; public class ReportTest { private static final String WRONG_MAXIMUM = "The maximum was computed or formatted incorrectly."; + private static final String WRONG_MINIMUM = "The minimum was computed or formatted incorrectly."; private File outputFile; private Report report; @@ -73,6 +74,24 @@ public class ReportTest { assertEquals(WRONG_MAXIMUM, "100.3000", report.getMaximum(valuesWithMaxTwice)); } + @Test + public void testGetMinimumReturnsNaNForEmptyList(){ + List<Double> values = new ArrayList<>(); + assertEquals("For empty lists, NaN should be returned", "NaN", report.getMinimum(values)); + } + + @Test + public void testGetMinimumReturnsMinimum(){ + final List<Double> values = Arrays.asList(3.0, -1.2, 100.0, 100.3, 0.07); + assertEquals(WRONG_MINIMUM, "-1.2000", report.getMinimum(values)); + final List<Double> valuesWithNegativeMin = Arrays.asList(-3.0, -1.2, -100.0, -100.3, -0.07); + assertEquals(WRONG_MINIMUM, "-100.3000", report.getMinimum(valuesWithNegativeMin)); + final List<Double> valuesWithSameNumberTwice = Arrays.asList(30.0, -1.2, 100.0, 100.3, -0.07, 100.0); + assertEquals(WRONG_MINIMUM, "-1.2000", report.getMinimum(valuesWithSameNumberTwice)); + final List<Double> valuesWithMinTwice = Arrays.asList(30.0, 1.2, 100.0, 100.3, 0.07, 100.3, 0.07); + assertEquals(WRONG_MINIMUM, "0.0700", report.getMinimum(valuesWithMinTwice)); + } + @After public void cleanUp() { SimScenario.reset(); diff --git a/toolkit/reportSummary/dataSyncAnalysis.py b/toolkit/reportSummary/dataSyncAnalysis.py index 0cae787ac5343627fd55376813926164e1bd32f8..f714e522849e5dda85b6ee434f5538392a8aa1d2 100644 --- a/toolkit/reportSummary/dataSyncAnalysis.py +++ b/toolkit/reportSummary/dataSyncAnalysis.py @@ -15,10 +15,10 @@ import matplotlib.ticker as ticker # (2) a path to save the resulting graphic to. # # Data sync stats for scenario realisticScenario -# sim_time: 60.00, avg_used_mem: 0.00%, max_used_mem: 0.00%, med_avg_data_util: 0.99, avg_data_util: 0.99, med_avg_data_age: 21.15, avg_data_age: 24.56, med_max_data_age: 21.15, med_avg_data_dist: 97.41, avg_data_dist: 90.05, med_max_data_dist: 97.41 +# sim_time: 60.00, avg_used_mem: 0.00%, min_used_mem: 0.00%, max_used_mem: 0.00%, med_avg_data_util: 0.99, avg_data_util: 0.99, med_avg_data_age: 21.15, avg_data_age: 24.56, med_max_data_age: 21.15, med_avg_data_dist: 97.41, avg_data_dist: 90.05, med_max_data_dist: 97.41 # avg_ratio_map: 0.00%, avg_ratio_marker: 29.41%, avg_ratio_skill: 47.06%, avg_ratio_res: 23.53% # -# sim_time: 120.10, avg_used_mem: 0.00%, max_used_mem: 0.00%, med_avg_data_util: 0.99, avg_data_util: 0.98, med_avg_data_age: 42.55, avg_data_age: 54.16, med_max_data_age: 42.55, med_avg_data_dist: 117.17, avg_data_dist: 146.08, med_max_data_dist: 117.17 +# sim_time: 120.10, avg_used_mem: 0.00%, min_used_mem: 0.00%, max_used_mem: 0.00%, med_avg_data_util: 0.99, avg_data_util: 0.98, med_avg_data_age: 42.55, avg_data_age: 54.16, med_max_data_age: 42.55, med_avg_data_dist: 117.17, avg_data_dist: 146.08, med_max_data_dist: 117.17 # avg_ratio_map: 0.00%, avg_ratio_marker: 23.64%, avg_ratio_skill: 32.73%, avg_ratio_res: 43.64% # ... @@ -30,6 +30,7 @@ def main(analysisFileName, graphicFileName): # Interpret lines to find memory consumption, data utility, data distance and data age metrics over time timePoints = [] averageMemoryConsumption = [] + minimumMemoryConsumption = [] maximumMemoryConsumption = [] medianAverageDataUtility = [] averageAverageDataUtility = [] @@ -42,7 +43,7 @@ def main(analysisFileName, graphicFileName): nextTimePoint = 0 for line in report: match = re.match("sim_time: (\d+.\d+), " - "avg_used_mem: (\d+.\d+)%, ""max_used_mem: (\d+.\d+)%, " + "avg_used_mem: (\d+.\d+)%, min_used_mem: (\d+.\d+)%, max_used_mem: (\d+.\d+)%, " "med_avg_data_util: (\d+.\d+), avg_data_util: (\d+.\d+), " "med_avg_data_age: (\d+.\d+), avg_data_age: (\d+.\d+), med_max_data_age: (\d+.\d+), " "med_avg_data_dist: (\d+.\d+), avg_data_dist: (\d+.\d+), med_max_data_dist: (\d+.\d+)", line) @@ -57,15 +58,16 @@ def main(analysisFileName, graphicFileName): # Make sure to use correct unit (minutes, kilometres) timePoints.append(timePoint) averageMemoryConsumption.append(float(match.group(2))) - maximumMemoryConsumption.append(float(match.group(3))) - medianAverageDataUtility.append(float(match.group(4))) - averageAverageDataUtility.append(float(match.group(5))) - medianAverageDataAge.append(float(match.group(6)) / 60) - averageAverageDataAge.append(float(match.group(7)) / 60) - medianMaximumDataAge.append(float(match.group(8)) / 60) - medianAverageDataDistance.append(float(match.group(9)) / 1000) - averageAverageDataDistance.append(float(match.group(10)) / 1000) - medianMaximumDataDistance.append(float(match.group(11)) / 1000) + minimumMemoryConsumption.append(float(match.group(3))) + maximumMemoryConsumption.append(float(match.group(4))) + medianAverageDataUtility.append(float(match.group(5))) + averageAverageDataUtility.append(float(match.group(6))) + medianAverageDataAge.append(float(match.group(7)) / 60) + averageAverageDataAge.append(float(match.group(8)) / 60) + medianMaximumDataAge.append(float(match.group(9)) / 60) + medianAverageDataDistance.append(float(match.group(10)) / 1000) + averageAverageDataDistance.append(float(match.group(11)) / 1000) + medianMaximumDataDistance.append(float(match.group(12)) / 1000) # Create four graphics over time: one each for memory consumption, data utility, data distance and data age fig = plt.figure(figsize=(12,12)) @@ -88,7 +90,9 @@ def main(analysisFileName, graphicFileName): addSubplot(3, title='Memory Consumption', ylabel='Used memory', - plots=[(averageMemoryConsumption, 'Average'), (maximumMemoryConsumption, 'Maximum')]) + plots=[(averageMemoryConsumption, 'Average'), + (maximumMemoryConsumption, 'Maximum'), + (minimumMemoryConsumption, 'Minimum')]) addSubplot(2, title='Average Data Utility in Local Database', ylabel='Average data utility', plots=[(medianAverageDataUtility, 'Median'), (averageAverageDataUtility, 'Average')])