From a041bf738bc62b4f2d6083e30bf1d2f07155488c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20K=C3=BChnel?= Date: Fri, 3 May 2024 14:27:17 +0200 Subject: [PATCH] update matsim (#91) * change java version to 21 * explicitly configure java version in maven compiler plugin * build(deps): bump com.google.errorprone:error_prone_annotations Bumps [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) from 2.27.0 to 2.27.1. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.27.0...v2.27.1) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_annotations dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * build(deps): bump org.checkerframework:checker-qual Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.42.0 to 3.43.0. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.42.0...checker-framework-3.43.0) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * build(deps): bump com.google.guava:guava from 33.1.0-jre to 33.2.0-jre Bumps [com.google.guava:guava](https://github.com/google/guava) from 33.1.0-jre to 33.2.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * replace population comparison in tests and deprecate by byte comparison of plan files (#3247); add attributes comparison --------- Signed-off-by: dependabot[bot] Co-authored-by: Paul Heinrich Co-authored-by: Paul Heinrich <55242299+paulheinr@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michal Maciejewski --- .github/workflows/code-coverage.yaml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/deploy-on-pr-merge.yaml | 2 +- .../workflows/deploy-on-release-created.yaml | 2 +- .github/workflows/deploy-weekly.yaml | 2 +- .github/workflows/full-integration.yaml | 2 +- .github/workflows/verify-push.yaml | 2 +- .../contrib/ev/example/RunEvExampleTest.java | 9 +- ...nEvExampleWithLTHConsumptionModelTest.java | 5 +- .../usecases/chessboard/RunChessboardIT.java | 2 +- .../run/RunWithParkingProxyIT.java | 7 +- .../contrib/pseudosimulation/RunPSimTest.java | 2 +- .../run/RoadPricingByConfigfileTest.java | 4 +- .../contrib/etaxi/run/RunETaxiScenarioIT.java | 3 +- .../taxi/optimizer/TaxiOptimizerTests.java | 3 +- .../java/playground/vsp/ev/UrbanEVIT.java | 5 +- .../core/population/PopulationUtils.java | 7 + .../routes/PopulationComparison.java | 296 +++++++++++------- .../attributable/AttributesComparison.java | 41 +++ .../modules/ExternalModuleTest.java | 13 +- .../org/matsim/core/router/RoutingIT.java | 7 +- .../integration/replanning/ReRoutingIT.java | 9 +- .../attributable/AttributesTest.java | 106 ++++++- pom.xml | 12 +- 24 files changed, 380 insertions(+), 165 deletions(-) create mode 100644 matsim/src/main/java/org/matsim/utils/objectattributes/attributable/AttributesComparison.java diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml index 8a7054763c5..a5a28e1fdef 100644 --- a/.github/workflows/code-coverage.yaml +++ b/.github/workflows/code-coverage.yaml @@ -17,7 +17,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7804cbe9423..c3601652607 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' diff --git a/.github/workflows/deploy-on-pr-merge.yaml b/.github/workflows/deploy-on-pr-merge.yaml index 11a60d440a6..44828163634 100644 --- a/.github/workflows/deploy-on-pr-merge.yaml +++ b/.github/workflows/deploy-on-pr-merge.yaml @@ -22,7 +22,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' server-id: 'matsim-releases' diff --git a/.github/workflows/deploy-on-release-created.yaml b/.github/workflows/deploy-on-release-created.yaml index ace4f1881fb..a490c614a91 100644 --- a/.github/workflows/deploy-on-release-created.yaml +++ b/.github/workflows/deploy-on-release-created.yaml @@ -16,7 +16,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' server-id: matsim-releases diff --git a/.github/workflows/deploy-weekly.yaml b/.github/workflows/deploy-weekly.yaml index 88e183083b7..f30f7371684 100644 --- a/.github/workflows/deploy-weekly.yaml +++ b/.github/workflows/deploy-weekly.yaml @@ -17,7 +17,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' server-id: 'matsim-releases' diff --git a/.github/workflows/full-integration.yaml b/.github/workflows/full-integration.yaml index e63fc75985b..a75ea5767da 100644 --- a/.github/workflows/full-integration.yaml +++ b/.github/workflows/full-integration.yaml @@ -27,7 +27,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' diff --git a/.github/workflows/verify-push.yaml b/.github/workflows/verify-push.yaml index 865c39583fe..f7f5bce3718 100644 --- a/.github/workflows/verify-push.yaml +++ b/.github/workflows/verify-push.yaml @@ -87,7 +87,7 @@ jobs: if: ${{matrix.module != 'matsim' || steps.detect-changes.outputs.outside-contribs == 'true'}} uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: 'maven' diff --git a/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleTest.java b/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleTest.java index 87644405c7d..9509bba85cc 100644 --- a/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleTest.java +++ b/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleTest.java @@ -9,6 +9,7 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.testcases.MatsimTestUtils; import org.matsim.utils.eventsfilecomparison.ComparisonResult; @@ -33,8 +34,8 @@ public class RunEvExampleTest{ Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ) ; PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_plans.xml.gz" ); - boolean result = PopulationUtils.comparePopulations( expected, actual ); - Assertions.assertTrue(result); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } { String expected = utils.getInputDirectory() + "/output_events.xml.gz" ; @@ -66,8 +67,8 @@ public class RunEvExampleTest{ Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ) ; PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_plans.xml.gz" ); - boolean result = PopulationUtils.comparePopulations( expected, actual ); - Assertions.assertTrue(result); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } { String expected = utils.getInputDirectory() + "/output_events.xml.gz" ; diff --git a/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest.java b/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest.java index 447b79966ad..c71b0898651 100644 --- a/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest.java +++ b/contribs/ev/src/test/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest.java @@ -9,6 +9,7 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.testcases.MatsimTestUtils; import org.matsim.utils.eventsfilecomparison.ComparisonResult; @@ -33,8 +34,8 @@ void runTest(){ Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ) ; PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_plans.xml.gz" ); - boolean result = PopulationUtils.comparePopulations( expected, actual ); - Assertions.assertTrue( result ); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } { String expected = utils.getInputDirectory() + "/output_events.xml.gz" ; diff --git a/contribs/freight/src/test/java/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT.java b/contribs/freight/src/test/java/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT.java index b195b3b89d7..37f4b4ca96c 100644 --- a/contribs/freight/src/test/java/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT.java +++ b/contribs/freight/src/test/java/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT.java @@ -54,7 +54,7 @@ void runChessboard() { Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ) ; PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_plans.xml.gz" ); - PopulationComparison.Result result = new PopulationComparison().compare(expected, actual); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); Assertions.assertSame(PopulationComparison.Result.equal, result); } { diff --git a/contribs/parking/src/test/java/org/matsim/contrib/parking/parkingproxy/run/RunWithParkingProxyIT.java b/contribs/parking/src/test/java/org/matsim/contrib/parking/parkingproxy/run/RunWithParkingProxyIT.java index e87a4afbaba..7619ded63e9 100644 --- a/contribs/parking/src/test/java/org/matsim/contrib/parking/parkingproxy/run/RunWithParkingProxyIT.java +++ b/contribs/parking/src/test/java/org/matsim/contrib/parking/parkingproxy/run/RunWithParkingProxyIT.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -27,6 +28,7 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; @@ -60,9 +62,8 @@ void testMain(){ PopulationUtils.readPopulation( expected, utils.getInputDirectory() + "/output_experienced_plans.xml.gz" ); final Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ); PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_experienced_plans.xml.gz" ); - if(!PopulationUtils.comparePopulations( expected, actual )) { - throw new RuntimeException("Plans file comparison ended with result false"); - } + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } } } diff --git a/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java b/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java index bd9e68ada04..40545474438 100644 --- a/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java +++ b/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java @@ -102,7 +102,7 @@ public void install() { PopulationUtils.readPopulation( popExpected, utils.getInputDirectory() + "/output_plans.xml.gz" ); Population popActual = PopulationUtils.createPopulation( config ); PopulationUtils.readPopulation( popActual, outDir + "/output_plans.xml.gz" ); - new PopulationComparison().compare( popExpected, popActual ) ; + PopulationComparison.compare( popExpected, popActual ) ; Assertions.assertEquals(138.86084460860525, psimScore, MatsimTestUtils.EPSILON, "RunPsim score changed."); } diff --git a/contribs/roadpricing/src/test/java/org/matsim/contrib/roadpricing/run/RoadPricingByConfigfileTest.java b/contribs/roadpricing/src/test/java/org/matsim/contrib/roadpricing/run/RoadPricingByConfigfileTest.java index e59e74dd90b..93893f362fc 100644 --- a/contribs/roadpricing/src/test/java/org/matsim/contrib/roadpricing/run/RoadPricingByConfigfileTest.java +++ b/contribs/roadpricing/src/test/java/org/matsim/contrib/roadpricing/run/RoadPricingByConfigfileTest.java @@ -29,6 +29,7 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; @@ -62,7 +63,8 @@ final void testMain() { PopulationUtils.readPopulation( expected, utils.getInputDirectory() + "/output_plans.xml.gz" ); final Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ); PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_plans.xml.gz" ); - Assertions.assertTrue(PopulationUtils.comparePopulations( expected, actual ), "Populations are different"); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } diff --git a/contribs/taxi/src/test/java/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT.java b/contribs/taxi/src/test/java/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT.java index 7138bf21c82..ed4f6a55e6f 100644 --- a/contribs/taxi/src/test/java/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT.java +++ b/contribs/taxi/src/test/java/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT.java @@ -68,8 +68,7 @@ private void runScenario(String configPath) { Population actual = PopulationUtils.createPopulation(ConfigUtils.createConfig()); PopulationUtils.readPopulation(actual, utils.getOutputDirectory() + "/output_plans.xml.gz"); - PopulationComparison populationComparison = new PopulationComparison(); - PopulationComparison.Result result = populationComparison.compare(expected, actual); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); Assertions.assertEquals(PopulationComparison.Result.equal, result); } { diff --git a/contribs/taxi/src/test/java/org/matsim/contrib/taxi/optimizer/TaxiOptimizerTests.java b/contribs/taxi/src/test/java/org/matsim/contrib/taxi/optimizer/TaxiOptimizerTests.java index 28dd7087e40..be3f77e3bd9 100644 --- a/contribs/taxi/src/test/java/org/matsim/contrib/taxi/optimizer/TaxiOptimizerTests.java +++ b/contribs/taxi/src/test/java/org/matsim/contrib/taxi/optimizer/TaxiOptimizerTests.java @@ -73,8 +73,7 @@ public static void runBenchmark(boolean vehicleDiversion, AbstractTaxiOptimizerP Population actual = PopulationUtils.createPopulation(ConfigUtils.createConfig()); PopulationUtils.readPopulation(actual, utils.getOutputDirectory() + "/output_plans.xml.gz"); - PopulationComparison populationComparison = new PopulationComparison(); - PopulationComparison.Result result = populationComparison.compare(expected, actual); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); Assertions.assertEquals(PopulationComparison.Result.equal, result); } { diff --git a/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java b/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java index b96c04fb079..b133b0e485a 100644 --- a/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java +++ b/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java @@ -8,6 +8,7 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.testcases.MatsimTestUtils; import org.matsim.utils.eventsfilecomparison.ComparisonResult; @@ -32,8 +33,8 @@ void run() { Population actual = PopulationUtils.createPopulation( ConfigUtils.createConfig() ) ; PopulationUtils.readPopulation( actual, utils.getOutputDirectory() + "/output_plans.xml.gz" ); - boolean result = PopulationUtils.comparePopulations( expected, actual ); - Assertions.assertTrue( result ); + PopulationComparison.Result result = PopulationComparison.compare(expected, actual); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } { String expected = utils.getInputDirectory() + "/output_events.xml.gz" ; diff --git a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java index cae206c534b..5755982b6ed 100644 --- a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java +++ b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java @@ -665,7 +665,10 @@ public static double calculateSimilarity(List activities1, List it1 = population1.getPersons().values().iterator(); - Iterator it2 = population2.getPersons().values().iterator(); - - while( it1.hasNext() || it2.hasNext() ) { - if ( ! it1.hasNext() ) { - result = Result.notEqual ; - return result ; - } - if ( ! it2.hasNext() ) { - result = Result.notEqual ; - return result ; - } - Person person1 = it1.next(); - Person person2 = it2.next(); - if ( !person1.getId().equals( person2.getId() ) ) { - log.warn( "persons out of sequence" ); - result = Result.notEqual ; - continue; - } - Plan plan1 = person1.getSelectedPlan(); - Plan plan2 = person2.getSelectedPlan(); - if ( Math.abs( plan1.getScore() - plan2.getScore() ) > 100.*Double.MIN_VALUE || - !equals(plan1.getPlanElements(), plan2.getPlanElements())) { - - double maxScore = Double.NEGATIVE_INFINITY; - for( Plan plan : person2.getPlans() ){ - if ( plan.getScore() > maxScore ) { - maxScore = plan.getScore() ; - } - } - - log.warn( "" ); - log.warn("personId=" + person1.getId() + "; score1=" + plan1.getScore() + "; score2=" + plan2.getScore() + "; maxScore2=" + maxScore ) ; - log.warn( "" ); - for( PlanElement planElement : plan1.getPlanElements() ){ - log.warn( planElement ); - } - log.warn( "" ); - for( PlanElement planElement : plan2.getPlanElements() ){ - log.warn( planElement ); - } - log.warn( "" ); - - } - } - return result ; - } - - - public static boolean equals(List planElements, - List planElements2) { - int nElements = planElements.size(); - if (nElements != planElements2.size()) { - return false; - } else { - for (int i = 0; i < nElements; i++) { - if (!equals(planElements.get(i), planElements2.get(i))) { - return false; - } - } - } - return true; - } - - /* Warning: This is NOT claimed to be correct. (It isn't.) - * - */ - private static boolean equals(PlanElement o1, PlanElement o2) { - if (o1 instanceof Leg) { - if (o2 instanceof Leg) { - Leg leg1 = (Leg) o1; - Leg leg2 = (Leg) o2; - if (!leg1.getDepartureTime().equals(leg2.getDepartureTime())) { - return false; - } - if (!leg1.getMode().equals(leg2.getMode())) { - return false; - } - if (!leg1.getTravelTime().equals(leg2.getTravelTime())) { - return false; - } - } else { - return false; - } - } else if (o1 instanceof Activity) { - if (o2 instanceof Activity) { - Activity activity1 = (Activity) o1; - Activity activity2 = (Activity) o2; - if (activity1.getEndTime().isUndefined() ^ activity2.getEndTime().isUndefined()) { - return false; - } - if (activity1.getEndTime().isDefined() && activity1.getEndTime().seconds() - != activity2.getEndTime().seconds()) { - return false; - } - if (activity1.getStartTime().isUndefined() ^ activity2.getStartTime().isUndefined()) { - return false; - } - if (activity1.getStartTime().isDefined() && activity1.getStartTime().seconds() - != activity2.getStartTime().seconds()) { - return false; - } - } else { - return false; - } - } else { - throw new RuntimeException ("Unexpected PlanElement"); - } - return true; - } +public class PopulationComparison { + + private final static double DEFAULT_DELTA = 1e-10; + + public enum Result {equal, notEqual} + + private static final Logger log = LogManager.getLogger(PopulationComparison.class); + + private PopulationComparison() { + } + + public static Result compare(Population population1, Population population2) { + return compare(population1, population2, DEFAULT_DELTA); + } + + + public static Result compare(Population population1, Population population2, double delta) { + Result result = Result.equal; + + Iterator it1 = population1.getPersons().values().iterator(); + Iterator it2 = population2.getPersons().values().iterator(); + + while (it1.hasNext() || it2.hasNext()) { + if (!it1.hasNext()) { + result = Result.notEqual; + log.warn(""); + log.warn(" different length in populations. "); + return result; + } + if (!it2.hasNext()) { + result = Result.notEqual; + log.warn(""); + log.warn(" different length in populations. "); + return result; + } + + Person person1 = it1.next(); + Person person2 = it2.next(); + + if (!person1.getId().equals(person2.getId())) { + log.warn(""); + log.warn("persons out of sequence p1: " + person1.getId() + " | p2: " + person2.getId()); + result = Result.notEqual; + continue; + } + + if (!AttributesComparison.equals(person1.getAttributes(), person2.getAttributes())) { + log.warn(""); + log.warn("person attributes different p1: " + person1.getId() + " | p2: " + person2.getId()); + } + + Plan plan1 = person1.getSelectedPlan(); + Plan plan2 = person2.getSelectedPlan(); + + if (!AttributesComparison.equals(plan1.getAttributes(), plan2.getAttributes())) { + log.warn(""); + log.warn("selected plan attributes different p1: " + person1.getId() + " | p2: " + person2.getId()); + } + + Optional score1 = Optional.ofNullable(plan1.getScore()); + Optional score2 = Optional.ofNullable(plan2.getScore()); + + if (score1.isPresent() && score2.isPresent()) { + if (!isWithinDelta(plan1.getScore(), plan2.getScore(), delta)) { + + double maxScore = Double.NEGATIVE_INFINITY; + for (Plan plan : person2.getPlans()) { + if (plan.getScore() > maxScore) { + maxScore = plan.getScore(); + } + } + + log.warn(""); + log.warn("personId=" + person1.getId() + "; score1=" + plan1.getScore() + "; score2=" + plan2.getScore() + "; maxScore2=" + maxScore); + log.warn(""); + + result = Result.notEqual; + + } + } else if (score1.isEmpty() && score2.isEmpty()) { + } else { + log.warn(""); + log.warn(" selected plan scores not consistently present: p1: " + person1.getId() + " | p2: " + person2.getId()); + result = Result.notEqual; + } + + if (!equals(plan1.getPlanElements(), plan2.getPlanElements())) { + log.warn(""); + log.warn(" selected plan elements not equal: p1: " + person1.getId() + " | p2: " + person2.getId()); + + for (PlanElement planElement : plan1.getPlanElements()) { + log.warn(planElement); + } + log.warn(""); + for (PlanElement planElement : plan2.getPlanElements()) { + log.warn(planElement); + } + log.warn(""); + result = Result.notEqual; + } + } + return result; + } + + private static boolean isWithinDelta(double double1, double double2, double delta) { + return Math.abs(double1 - double2) < delta; + } + + private static boolean isBothUndefinedOrWithinDelta(OptionalTime time1, OptionalTime time2, double delta) { + if (time1.isUndefined() ^ time2.isUndefined()) { + return false; + } else if(time1.isDefined()) { + return isWithinDelta((int) time1.seconds(), (int) time2.seconds(), delta); + } + return true; + } + + public static boolean equals(List planElements, + List planElements2) { + return equals(planElements, planElements2, DEFAULT_DELTA); + } + + public static boolean equals(List planElements, + List planElements2, double delta) { + int nElements = planElements.size(); + if (nElements != planElements2.size()) { + return false; + } else { + for (int i = 0; i < nElements; i++) { + if (!equals(planElements.get(i), planElements2.get(i), delta)) { + return false; + } + } + } + return true; + } + + /* Warning: This is NOT claimed to be correct. (It isn't.) + * + */ + private static boolean equals(PlanElement o1, PlanElement o2, double delta) { + if (!AttributesComparison.equals(o1.getAttributes(), o2.getAttributes())) { + return false; + } + if (o1 instanceof Leg leg1) { + if (o2 instanceof Leg leg2) { + if(!isBothUndefinedOrWithinDelta(leg1.getDepartureTime(), leg2.getDepartureTime(), delta)) { + return false; + } + if (!leg1.getMode().equals(leg2.getMode())) { + return false; + } + if (!isBothUndefinedOrWithinDelta(leg1.getTravelTime(), leg2.getTravelTime(), delta)) { + return false; + } + } else { + return false; + } + } else if (o1 instanceof Activity activity1) { + if (o2 instanceof Activity activity2) { + if (!isBothUndefinedOrWithinDelta(activity1.getEndTime(), activity2.getEndTime(), delta)) { + return false; + } + if (!isBothUndefinedOrWithinDelta(activity1.getStartTime(), activity2.getStartTime(), delta)) { + return false; + } + } else { + return false; + } + } else { + throw new RuntimeException("Unexpected PlanElement"); + } + return true; + } } diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributable/AttributesComparison.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributable/AttributesComparison.java new file mode 100644 index 00000000000..12047efa57d --- /dev/null +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributable/AttributesComparison.java @@ -0,0 +1,41 @@ +package org.matsim.utils.objectattributes.attributable; + +import com.google.common.base.Equivalence; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.matsim.vehicles.PersonVehicles; + +import java.util.Map; + +/** + * @author nkuehnel / MOIA + */ +public final class AttributesComparison { + + private AttributesComparison(){} + + public static boolean equals(Attributes a1, Attributes a2) { + if(a1.size() != a2.size()) { + return false; + } + + return Maps.difference(a1.getAsMap(), a2.getAsMap(), new CustomEquivalence()).areEqual(); + } + + private static class CustomEquivalence extends Equivalence { + @Override + protected boolean doEquivalent(Object a, Object b) { + if (a instanceof Map mapA && b instanceof Map mapB) { + return Maps.difference(mapA, mapB, new CustomEquivalence()).areEqual(); + } else if (a instanceof PersonVehicles vehiclesA && b instanceof PersonVehicles vehiclesB) { + return Maps.difference(vehiclesA.getModeVehicles(), vehiclesB.getModeVehicles(), new CustomEquivalence()).areEqual(); + } + return a.equals(b); + } + + @Override + protected int doHash(Object o) { + return o.hashCode(); + } + } +} diff --git a/matsim/src/test/java/org/matsim/core/replanning/modules/ExternalModuleTest.java b/matsim/src/test/java/org/matsim/core/replanning/modules/ExternalModuleTest.java index e9c44723123..23cacc3fb67 100644 --- a/matsim/src/test/java/org/matsim/core/replanning/modules/ExternalModuleTest.java +++ b/matsim/src/test/java/org/matsim/core/replanning/modules/ExternalModuleTest.java @@ -22,8 +22,8 @@ package org.matsim.core.replanning.modules; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.matsim.api.core.v01.Scenario; @@ -32,9 +32,9 @@ import org.matsim.api.core.v01.population.PopulationWriter; import org.matsim.api.core.v01.replanning.PlanStrategyModule; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.controler.*; -import org.matsim.core.population.PopulationUtils; +import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.population.io.PopulationReader; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.core.replanning.ReplanningContext; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.testcases.MatsimTestUtils; @@ -82,7 +82,9 @@ public boolean invoke() { } }, "test", outputDirectoryHierarchy, scenario); replanPopulation(scenario.getPopulation(), testee); - Assertions.assertTrue(PopulationUtils.equalPopulation(scenario.getPopulation(), originalScenario.getPopulation())); + + PopulationComparison.Result result = PopulationComparison.compare(scenario.getPopulation(), originalScenario.getPopulation()); + Assertions.assertEquals(PopulationComparison.Result.equal, result); } @Test @@ -99,7 +101,8 @@ public boolean invoke() { } }, "test", outputDirectoryHierarchy, scenario); replanPopulation(scenario.getPopulation(), testee); - Assertions.assertFalse(PopulationUtils.equalPopulation(scenario.getPopulation(), originalScenario.getPopulation())); + PopulationComparison.Result result = PopulationComparison.compare(scenario.getPopulation(), originalScenario.getPopulation()); + Assertions.assertNotEquals(PopulationComparison.Result.equal, result); } private Population loadPopulation(String filename) { diff --git a/matsim/src/test/java/org/matsim/core/router/RoutingIT.java b/matsim/src/test/java/org/matsim/core/router/RoutingIT.java index 35ac550ee68..7d3ace29d49 100644 --- a/matsim/src/test/java/org/matsim/core/router/RoutingIT.java +++ b/matsim/src/test/java/org/matsim/core/router/RoutingIT.java @@ -39,6 +39,7 @@ import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.PersonAlgorithm; import org.matsim.core.population.io.PopulationReader; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.core.router.costcalculators.FreespeedTravelTimeAndDisutility; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.speedy.SpeedyALTFactory; @@ -161,12 +162,12 @@ private void doTest(final RouterProvider provider) { new MatsimNetworkReader(referenceScenario.getNetwork()).readFile(config.network().getInputFile()); new PopulationReader(referenceScenario).readFile(inPlansName); - final boolean isEqual = PopulationUtils.equalPopulation(referenceScenario.getPopulation(), scenario.getPopulation()); - if ( !isEqual ) { + PopulationComparison.Result result = PopulationComparison.compare(referenceScenario.getPopulation(), scenario.getPopulation()); + if (result == PopulationComparison.Result.notEqual) { new PopulationWriter(referenceScenario.getPopulation(), scenario.getNetwork()).write(this.utils.getOutputDirectory() + "/reference_population.xml.gz"); new PopulationWriter(scenario.getPopulation(), scenario.getNetwork()).write(this.utils.getOutputDirectory() + "/output_population.xml.gz"); } - Assertions.assertTrue(isEqual, "different plans files."); + Assertions.assertEquals(PopulationComparison.Result.equal, result, "different plans files"); } private static void calcRoute( diff --git a/matsim/src/test/java/org/matsim/integration/replanning/ReRoutingIT.java b/matsim/src/test/java/org/matsim/integration/replanning/ReRoutingIT.java index c432538434a..f318a535671 100644 --- a/matsim/src/test/java/org/matsim/integration/replanning/ReRoutingIT.java +++ b/matsim/src/test/java/org/matsim/integration/replanning/ReRoutingIT.java @@ -37,6 +37,7 @@ import org.matsim.core.controler.Controler; import org.matsim.core.gbl.Gbl; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.routes.PopulationComparison; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; @@ -113,7 +114,6 @@ private void evaluate() throws MalformedURLException { this.evaluate("plans.xml.gz"); } - private final static Logger LOG = LogManager.getLogger(ReRoutingIT.class); private void evaluate(String plansFilename) throws MalformedURLException { Config config = utils.loadConfig(utils.getClassInputDirectory() + "config.xml"); config.network().setInputFile(IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("berlin"), "network.xml.gz").toString()); @@ -124,13 +124,12 @@ private void evaluate(String plansFilename) throws MalformedURLException { Scenario scenario = ScenarioUtils.loadScenario(config); Gbl.startMeasurement(); - final boolean isEqual = PopulationUtils.equalPopulation(referenceScenario.getPopulation(), scenario.getPopulation()); + PopulationComparison.Result result = PopulationComparison.compare(referenceScenario.getPopulation(), scenario.getPopulation()); Gbl.printElapsedTime(); - if ( !isEqual ) { + if (result == PopulationComparison.Result.notEqual) { new PopulationWriter(referenceScenario.getPopulation(), scenario.getNetwork()).write(utils.getOutputDirectory() + "/reference_population.xml.gz"); new PopulationWriter(scenario.getPopulation(), scenario.getNetwork()).write(utils.getOutputDirectory() + "/output_population.xml.gz"); } - Assertions.assertTrue(isEqual, "different plans files."); + Assertions.assertEquals(PopulationComparison.Result.equal, result, "different plans file"); } - } diff --git a/matsim/src/test/java/org/matsim/utils/objectattributes/attributable/AttributesTest.java b/matsim/src/test/java/org/matsim/utils/objectattributes/attributable/AttributesTest.java index 353c7b5476b..b5740b93605 100644 --- a/matsim/src/test/java/org/matsim/utils/objectattributes/attributable/AttributesTest.java +++ b/matsim/src/test/java/org/matsim/utils/objectattributes/attributable/AttributesTest.java @@ -24,11 +24,9 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; +import java.util.*; - /** +/** * @author thibautd */ public class AttributesTest { @@ -148,4 +146,104 @@ void testGetAsMap() { Assertions.fail("Expected NoSuchElementException, but caught a different one."); } } + + @Test + void testComparison() { + + AttributesImpl a1 = new AttributesImpl(); + AttributesImpl a2 = new AttributesImpl(); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + a1.putAttribute("att1", "1"); + Assertions.assertFalse(AttributesComparison.equals(a1, a2)); + + a2.putAttribute("att1", "1"); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + a2.putAttribute("att1", "one"); + Assertions.assertFalse(AttributesComparison.equals(a1, a2)); + + } + + @Test + void testComplexComparisonMap() { + + AttributesImpl a1 = new AttributesImpl(); + AttributesImpl a2 = new AttributesImpl(); + + Map map = new HashMap<>(); + a1.putAttribute("map", map); + a2.putAttribute("map", map); + + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + Map map2 = new HashMap<>(); + a2.putAttribute("map", map2); + + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + Object o = new Object(); + map.put(o, o); + Assertions.assertFalse(AttributesComparison.equals(a1, a2)); + + map2.put(o, o); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + + Map recursiveMap = new HashMap<>(); + map.put("recursiveMap", recursiveMap); + map2.put("recursiveMap", recursiveMap); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + recursiveMap.put(o, o); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + Map recursiveMap2 = new HashMap<>(); + map2.put("recursiveMap", recursiveMap2); + Assertions.assertFalse(AttributesComparison.equals(a1, a2)); + + recursiveMap2.put(o, o); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + } + + @Test + void testComplexComparisonList() { + + AttributesImpl a1 = new AttributesImpl(); + AttributesImpl a2 = new AttributesImpl(); + + List list= new ArrayList<>(); + a1.putAttribute("list", list); + a2.putAttribute("list", list); + + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + List list2 = new ArrayList<>(); + a2.putAttribute("list", list2); + + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + Object o = new Object(); + list.add(o); + Assertions.assertFalse(AttributesComparison.equals(a1, a2)); + + list2.add(o); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + List recursiveList = new ArrayList<>(); + list.add(recursiveList); + list2.add(recursiveList); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + recursiveList.add(o); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + + List recursiveList2 = new ArrayList<>(); + list2.add(recursiveList2); + Assertions.assertFalse(AttributesComparison.equals(a1, a2)); + list2.remove(recursiveList); + + recursiveList2.add(o); + Assertions.assertTrue(AttributesComparison.equals(a1, a2)); + } } diff --git a/pom.xml b/pom.xml index f5dc8484acc..e2a0c1c1c6f 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ UTF-8 UTF-8 - 17 + 21 2.23.1 @@ -103,7 +103,7 @@ com.google.guava guava - 33.1.0-jre + 33.2.0-jre org.apache.commons @@ -200,7 +200,7 @@ com.google.errorprone error_prone_annotations - 2.27.0 + 2.27.1 @@ -291,7 +291,7 @@ org.checkerframework checker-qual - 3.42.0 + 3.43.0 @@ -366,6 +366,10 @@ org.apache.maven.plugins maven-compiler-plugin 3.13.0 + + 21 + 21 + org.apache.maven.plugins