From 1d86390be7284bb55fcda64c15a23d455066bd65 Mon Sep 17 00:00:00 2001 From: Stefano Furi Date: Mon, 19 Jan 2026 12:40:47 +0100 Subject: [PATCH] feat!: add a reactive engine (#5066) --- .github/workflows/build-and-deploy.yml | 50 +- .github/workflows/update-ancillary-files.yml | 7 +- .gitignore | 1 + .idea/inspectionProfiles/Project_Default.xml | 4 + CHANGELOG.md | 92 + alchemist-api/build.gradle.kts | 1 + .../it/unibo/alchemist/model/Condition.java | 24 +- .../it/unibo/alchemist/model/Actionable.kt | 15 + .../it/unibo/alchemist/model/Environment.kt | 34 + .../kotlin/it/unibo/alchemist/model/Node.kt | 31 + .../model/observation/DerivedObservable.kt | 98 + .../alchemist/model/observation/Disposable.kt | 21 + .../model/observation/EventObservable.kt | 35 + .../alchemist/model/observation/Observable.kt | 227 +++ .../model/observation/ObservableExtensions.kt | 343 ++++ .../model/observation/ObservableList.kt | 267 +++ .../model/observation/ObservableMap.kt | 252 +++ .../model/observation/ObservableSet.kt | 232 +++ .../model/observation/ObservableListTest.kt | 247 +++ .../model/observation/ObservableMapTest.kt | 129 ++ .../model/observation/ObservableSetTest.kt | 181 ++ .../model/observation/ObservableTest.kt | 541 ++++++ .../it/unibo/alchemist/core/AbstractEngine.kt | 371 ++++ .../kotlin/it/unibo/alchemist/core/Engine.kt | 337 +--- .../it/unibo/alchemist/core/ReactiveEngine.kt | 116 ++ .../alchemist/core/AbstractDependencyTest.kt | 85 + .../alchemist/core/AbstractEngineTest.kt | 75 + .../alchemist/core/TestDependencyGraph.kt | 114 +- .../alchemist/core/TestEngineComparison.kt | 146 ++ .../core/TestEngineImplementation.kt | 12 + .../core/TestReactiveDependencies.kt | 46 + .../core/TestReactiveEngineImplementation.kt | 12 + .../alchemist/core/util/DependencyUtils.kt | 94 + .../model/conditions/AbstractCondition.java | 107 +- .../conditions/ConcentrationChanged.java | 46 +- .../conditions/MoleculeHasConcentration.java | 13 +- .../model/linkingrules/AdaptiveRange.java | 3 +- .../model/linkingrules/ClosestN.java | 2 +- .../model/reactions/AbstractReaction.java | 179 +- .../model/reactions/ChemicalReaction.java | 2 +- ...tractNonPropensityContributingCondition.kt | 4 +- .../model/conditions/ContainsMolecule.kt | 11 +- .../conditions/NeighborHasConcentration.kt | 54 + .../conditions/NoOtherReactionCanExecute.kt | 17 +- .../model/environments/AbstractEnvironment.kt | 167 +- .../alchemist/model/nodes/GenericNode.kt | 32 +- .../conditions/AbstractNeighborCondition.java | 64 +- .../conditions/BiomolPresentInEnv.java | 74 +- .../conditions/BiomolPresentInNeighbor.java | 60 +- .../biochemistry/conditions/EnvPresent.java | 18 +- .../conditions/GenericMoleculePresent.java | 52 +- .../conditions/GenericMoleculeUnderLevel.java | 41 +- .../conditions/JunctionPresentInCell.java | 38 +- .../conditions/NeighborhoodPresent.java | 20 +- .../conditions/TensionPresent.java | 101 +- .../environments/BioRect2DEnvironment.java | 2 +- .../model/biochemistry/CellProperty.kt | 91 +- .../model/biochemistry/properties/Cell.kt | 4 +- .../biochemistry/properties/CircularCell.kt | 4 +- .../properties/CircularDeformableCell.kt | 4 +- .../TestNeighborhoodReactionsPropensities.kt | 6 +- .../ComputationalRoundComplete.java | 26 +- .../model/protelis/ProtelisIncarnation.kt | 11 + .../protelis/actions/RunProtelisProgram.kt | 18 +- .../alchemist/model/sapere/ILsaNode.java | 6 + .../conditions/LsaNeighborhoodCondition.java | 48 +- .../conditions/LsaStandardCondition.java | 23 +- .../alchemist/model/sapere/nodes/LsaNode.java | 103 +- .../sapere/reactions/SAPEREGradient.java | 31 +- .../sapere/reactions/SAPEREReaction.java | 8 +- .../actions/RunScafiProgram.scala | 11 +- .../ScafiComputationalRoundComplete.scala | 1 + alchemist-loading/build.gradle.kts | 1 - .../boundary/loader/LoadingSystem.kt | 8 +- .../test/TestReactiveEngineLoading.kt | 41 + .../LinkNodesWithinRoutingRange.java | 3 +- .../model/physics/reactions/PhysicsUpdate.kt | 48 +- .../alchemist/test/GlobalTestReaction.kt | 18 + build.gradle.kts | 3 + .../{1.5.21.list => 1.5.22.list} | 0 .../caffeine/3.2.3.list | 3 - .../{4.21.0.list => 4.22.0.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 .../{42.3.18.list => 42.3.22.list} | 0 dokka-cache/no-javadoc.json | 772 -------- ...20913-r.list => 7.5.0.202512021534-r.list} | 0 dokka-cache/org.jooq/jool/0.9.15.list | 9 - .../mockito-core/{5.20.0.list => 5.21.0.list} | 0 .../{5.6.1.list => 5.6.2.list} | 0 .../{18.5.0.list => 18.5.2.list} | 0 .../{18.5.0.list => 18.5.2.list} | 0 gradle/libs.versions.toml | 28 +- kotlin-js-store/yarn.lock | 16 +- package-lock.json | 1644 +++++------------ package.json | 2 +- run_all_tests_reactive.sh | 2 + settings.gradle.kts | 4 +- 117 files changed, 5407 insertions(+), 2937 deletions(-) create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/DerivedObservable.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/Disposable.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/EventObservable.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/Observable.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/ObservableExtensions.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/ObservableList.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/ObservableMap.kt create mode 100644 alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/ObservableSet.kt create mode 100644 alchemist-api/src/test/kotlin/it/unibo/alchemist/model/observation/ObservableListTest.kt create mode 100644 alchemist-api/src/test/kotlin/it/unibo/alchemist/model/observation/ObservableMapTest.kt create mode 100644 alchemist-api/src/test/kotlin/it/unibo/alchemist/model/observation/ObservableSetTest.kt create mode 100644 alchemist-api/src/test/kotlin/it/unibo/alchemist/model/observation/ObservableTest.kt create mode 100644 alchemist-engine/src/main/kotlin/it/unibo/alchemist/core/AbstractEngine.kt create mode 100644 alchemist-engine/src/main/kotlin/it/unibo/alchemist/core/ReactiveEngine.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/AbstractDependencyTest.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/AbstractEngineTest.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/TestEngineComparison.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/TestEngineImplementation.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/TestReactiveDependencies.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/TestReactiveEngineImplementation.kt create mode 100644 alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/util/DependencyUtils.kt create mode 100644 alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/conditions/NeighborHasConcentration.kt create mode 100644 alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestReactiveEngineLoading.kt rename dokka-cache/ch.qos.logback/logback-classic/{1.5.21.list => 1.5.22.list} (100%) delete mode 100644 dokka-cache/com.github.ben-manes.caffeine/caffeine/3.2.3.list rename dokka-cache/de.flapdoodle.embed/de.flapdoodle.embed.mongo/{4.21.0.list => 4.22.0.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-api/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-cognitive-agents/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-composeui/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-engine/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-euclidean-geometry/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-full/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-graphql-surrogates/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-graphql/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-implementationbase/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-incarnation-biochemistry/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-incarnation-protelis/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-incarnation-sapere/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-loading/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-maintenance-tooling/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-maps/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-physics/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-sapere-mathexp/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-smartcam/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-swingui/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-test/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-ui-tooling/{42.3.18.list => 42.3.22.list} (100%) rename dokka-cache/it.unibo.alchemist/alchemist-web-renderer/{42.3.18.list => 42.3.22.list} (100%) delete mode 100644 dokka-cache/no-javadoc.json rename dokka-cache/org.eclipse.jgit/org.eclipse.jgit/{7.4.0.202509020913-r.list => 7.5.0.202512021534-r.list} (100%) delete mode 100644 dokka-cache/org.jooq/jool/0.9.15.list rename dokka-cache/org.mockito/mockito-core/{5.20.0.list => 5.21.0.list} (100%) rename dokka-cache/org.mongodb/mongodb-driver-sync/{5.6.1.list => 5.6.2.list} (100%) rename dokka-cache/org.protelis/protelis-interpreter/{18.5.0.list => 18.5.2.list} (100%) rename dokka-cache/org.protelis/protelis-lang/{18.5.0.list => 18.5.2.list} (100%) create mode 100755 run_all_tests_reactive.sh diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index fee409487f..b521ac8252 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -22,12 +22,12 @@ jobs: id: ci-preparation run: echo "build-ref=${{ github.workflow }}-${{ github.event.number || github.ref }}" >> $GITHUB_OUTPUT - name: Checkout - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Compute the Java version id: java-version run: ruby -e 'puts("java-version=#{/^minJavaVersion\s*=\s*(\d+)$/.match(File.read("gradle.properties"))[1]}")' >> $GITHUB_OUTPUT - name: Install Node - uses: actions/setup-node@v6.0.0 + uses: actions/setup-node@v6.1.0 with: node-version-file: package.json - name: Compute the next release version @@ -57,8 +57,8 @@ jobs: timeout-minutes: 180 steps: - name: Checkout - uses: actions/checkout@v6.0.0 - - uses: DanySK/build-check-deploy-gradle-action@3a800c3b221073eeae25f5fb98687e3a40aa1712 # 4.0.12 + uses: actions/checkout@v6.0.1 + - uses: DanySK/build-check-deploy-gradle-action@810719541546fff1db1edd1fb858e12654dc1d95 # 4.0.17 with: pre-build-command: ./gradlew kotlinUpgradeYarnLock build-command: true @@ -76,8 +76,8 @@ jobs: timeout-minutes: 30 steps: - name: Checkout - uses: danysk/action-checkout@733b8984412e3cc1ea66fcd20281710dc2118187 # 0.2.26 - - uses: DanySK/build-check-deploy-gradle-action@3a800c3b221073eeae25f5fb98687e3a40aa1712 # 4.0.12 + uses: danysk/action-checkout@7f6ee3cd5bd670cdb3e162894776736254afff1e # 0.2.27 + - uses: DanySK/build-check-deploy-gradle-action@810719541546fff1db1edd1fb858e12654dc1d95 # 4.0.17 with: retries-on-failure: 5 wait-between-retries: 120 @@ -91,7 +91,7 @@ jobs: - name: Tar files run: tar -cvf website.tar build/website - name: Upload website - uses: actions/upload-artifact@v5.0.0 + uses: actions/upload-artifact@v6.0.0 with: name: website-${{ github.sha }} path: website.tar @@ -131,8 +131,8 @@ jobs: timeout-minutes: 120 steps: - name: Checkout with full history - uses: danysk/action-checkout@733b8984412e3cc1ea66fcd20281710dc2118187 # 0.2.26 - - uses: DanySK/build-check-deploy-gradle-action@3a800c3b221073eeae25f5fb98687e3a40aa1712 # 4.0.12 + uses: danysk/action-checkout@7f6ee3cd5bd670cdb3e162894776736254afff1e # 0.2.27 + - uses: DanySK/build-check-deploy-gradle-action@810719541546fff1db1edd1fb858e12654dc1d95 # 4.0.17 with: signing-key: ${{ secrets.SIGNING_KEY }} signing-password: ${{ secrets.SIGNING_PASSWORD }} @@ -151,20 +151,20 @@ jobs: fi - name: Upload fatjars built on Linux if: ${{ runner.os == 'Linux' }} - uses: actions/upload-artifact@v5.0.0 + uses: actions/upload-artifact@v6.0.0 with: name: fat-jars-${{ github.sha }} path: build/shadow/* if-no-files-found: error - name: Upload the PKGBUILD if: ${{ runner.os == 'Linux' }} - uses: actions/upload-artifact@v5.0.0 + uses: actions/upload-artifact@v6.0.0 with: name: pkgbuild-${{ github.sha }} path: build/pkgbuild/PKGBUILD if-no-files-found: error - name: Upload platform-specific packages - uses: actions/upload-artifact@v5.0.0 + uses: actions/upload-artifact@v6.0.0 with: name: installer-package-${{ github.sha }}-${{ runner.os }} path: build/package/* @@ -179,7 +179,7 @@ jobs: always() && needs.assemble-and-upload.result == 'success' steps: - name: Download packages - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: name: installer-package-${{ github.sha }}-${{ runner.os }} - name: Install packages @@ -214,7 +214,7 @@ jobs: always() && needs.assemble-and-upload.result == 'success' steps: - name: Download packages - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: name: installer-package-${{ github.sha }}-${{ runner.os }} - name: Work around xdg bug https://bugs.archlinux.org/task/33316 @@ -242,7 +242,7 @@ jobs: image: fedora:44 steps: - name: Download packages - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: name: installer-package-${{ github.sha }}-${{ runner.os }} - name: Install packages @@ -275,7 +275,7 @@ jobs: # Work around https://github.com/actions/checkout/issues/1169 # sudo git config --system --add safe.directory /__w/Alchemist/Alchemist - name: Download the PKGBUILD - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: pattern: pkgbuild-${{ github.sha }} merge-multiple: true @@ -284,7 +284,7 @@ jobs: namcap PKGBUILD 2>&1 namcap PKGBUILD 2>&1 | awk 'END { exit (NR > 0 ? NR : 0) }' - name: Download the RPM - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: pattern: installer-package-${{ github.sha }}-${{ runner.os }} merge-multiple: true @@ -352,7 +352,7 @@ jobs: ) steps: - name: Checkout - uses: actions/checkout@v6.0.0 + uses: actions/checkout@v6.0.1 with: fetch-depth: 0 fetch-tags: 'true' @@ -360,7 +360,7 @@ jobs: token: ${{ secrets.DEPLOYMENT_TOKEN }} - name: Checkout the docs if: contains(github.repository, 'AlchemistSimulator/Alchemist') - uses: actions/checkout@v6.0.0 + uses: DanySK/checkout-classic@f55ca46048a09d13b47f0e80db6b546977327791 # 1.0.0 with: path: website fetch-depth: 0 @@ -369,32 +369,32 @@ jobs: repository: AlchemistSimulator/alchemistsimulator.github.io token: ${{ secrets.DEPLOYMENT_TOKEN }} - name: Download website artifact - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: name: website-${{ github.sha }} - name: Download packages - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: pattern: installer-package-${{ github.sha }}* path: build/package/ merge-multiple: true - name: Download fatJars - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: name: fat-jars-${{ github.sha }} path: build/shadow/ merge-multiple: true - name: Download the PKGBUILD - uses: actions/download-artifact@v6.0.0 + uses: actions/download-artifact@v7.0.0 with: pattern: pkgbuild-${{ github.sha }} path: pkgbuild/ merge-multiple: true - name: Install Node - uses: actions/setup-node@v6.0.0 + uses: actions/setup-node@v6.1.0 with: node-version-file: package.json - - uses: DanySK/build-check-deploy-gradle-action@3a800c3b221073eeae25f5fb98687e3a40aa1712 # 4.0.12 + - uses: DanySK/build-check-deploy-gradle-action@810719541546fff1db1edd1fb858e12654dc1d95 # 4.0.17 env: MAKEPKG_IMAGE: ${{ needs.ci-preparation.outputs.makepkg-image }} with: diff --git a/.github/workflows/update-ancillary-files.yml b/.github/workflows/update-ancillary-files.yml index 853475c9d2..293751a82c 100644 --- a/.github/workflows/update-ancillary-files.yml +++ b/.github/workflows/update-ancillary-files.yml @@ -3,6 +3,8 @@ on: push: branches: - master + paths: + - gradle/libs.versions.toml workflow_dispatch: jobs: @@ -12,14 +14,13 @@ jobs: group: javadoc-io-${{ github.workflow }}-${{ github.event.number || github.ref }} steps: - name: Checkout - uses: danysk/action-checkout@733b8984412e3cc1ea66fcd20281710dc2118187 # 0.2.26 + uses: danysk/action-checkout@7f6ee3cd5bd670cdb3e162894776736254afff1e # 0.2.27 with: token: ${{ secrets.DEPLOYMENT_TOKEN }} - - uses: DanySK/build-check-deploy-gradle-action@3a800c3b221073eeae25f5fb98687e3a40aa1712 # 4.0.12 + - uses: DanySK/build-check-deploy-gradle-action@810719541546fff1db1edd1fb858e12654dc1d95 # 4.0.17 with: pre-build-command: rm -rf dokka-cache build-command: | - ./gradlew dokkaGenerateModuleHtml dokkaGeneratePublicationHtml --dry-run for i in {1..3}; do rm dokka-cache/no-javadoc.json || true ./gradlew dokkaGenerateModuleHtml dokkaGeneratePublicationHtml --dry-run diff --git a/.gitignore b/.gitignore index fc3b089b4e..4257e9f0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ buildProfile/ .DS_Store # Other +dokka-cache/no-javadoc.json node_modules/ .kotlintest/ **/*.kotlintest*/* diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 7bee31df84..8dce9e5fbf 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -5,6 +5,10 @@