diff --git a/.github/workflows/test-case-studies.yml b/.github/workflows/test-case-studies.yml index 1230219a..57044816 100644 --- a/.github/workflows/test-case-studies.yml +++ b/.github/workflows/test-case-studies.yml @@ -16,7 +16,8 @@ jobs: strategy: matrix: jdk_distributions: [temurin, corretto, zulu] - jdk_version: ["17", "19"] + jdk_version: ["17", "21"] + fail-fast: false runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -48,178 +49,4 @@ jobs: name: test case logs path: | log.html - report.html - # - name: Cache-out java imodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/imodules - # key: java-imodules-${{ github.ref }}-${{ matrix.jabba_jdk }} - # - name: Cache-out java emodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/emodules - # key: java-emodules-${{ github.ref }}-${{ matrix.jabba_jdk }} - # build-scala: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v3 - # - name: Build scala - # run: | - # sudo apt-get update - # sudo apt-get install -y curl bash build-essential libssl-dev pkg-config dos2unix - # cd ${{ github.workspace }} - - # - name: Cache-out scala imodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/imodules - # key: scala-imodules-${{ github.ref }} - # - name: Cache-out scala emodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/emodules - # key: scala-emodules-${{ github.ref }} - # build-rust: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v3 - # - name: Build rust - # run: | - # sudo apt-get update - # sudo apt-get install -y curl bash build-essential libssl-dev pkg-config mingw-w64 musl-dev musl-tools - # cd ${{ github.workspace }} - # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable -y - # source "$HOME/.cargo/env" - # cargo build --release - # cp ./target/release/idesyde-orchestration idesyde - # - name: Cache-out rust - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/idesyde - # key: idesyde-${{ github.ref }} - # test-on-linux: - # strategy: - # matrix: - # jabba_jdk: [temurin@17, amazon-corretto@17, zulu@17, microsoft@17] - # runs-on: ubuntu-latest - # needs: [build-java, build-scala, build-rust] - # steps: - # - uses: actions/checkout@v3 - # - uses: actions/setup-python@v4 - # with: - # python-version: '3.10' - # - name: Cache-in java imodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/java-imodules - # key: java-imodules-${{ github.ref }}-${{ matrix.jabba_jdk }} - # restore-keys: | - # java-imodules-${{ github.ref }}- - # java-imodules- - # - name: Cache-in java emodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/java-emodules - # key: java-emodules-${{ github.ref }}-${{ matrix.jabba_jdk }} - # restore-keys: | - # java-emodules-${{ github.ref }}- - # java-emodules- - # - name: Cache-in scala imodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/scala-imodules - # key: scala-imodules-${{ github.ref }} - # restore-keys: | - # scala-imodules- - # - name: Cache-in scala emodules - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/scala-emodules - # key: scala-emodules-${{ github.ref }} - # restore-keys: | - # scala-emodules- - # - name: Cache-in rust - # uses: actions/cache@v3 - # with: - # path: ${{ github.workspace }}/idesyde - # key: idesyde-${{ github.ref }} - # - name: Build and Test cases - # continue-on-error: true - # run: | - # sudo apt-get update - # sudo apt-get install -y curl bash build-essential libssl-dev pkg-config mingw-w64 musl-dev musl-tools dos2unix - # cd ${{ github.workspace }} - # mkdir -p imodules - # mkdir -p emodules - # cp -r java-imodules/* imodules/ - # cp -r scala-imodules/* imodules/ - # cp -r java-emodules/* emodules/ - # cp -r scala-emodules/* emodules/ - # python -m pip install robotframework - # python -m robot --exclude slow TestsBenchmark.robot - # - name: Upload Robot Log - # uses: actions/upload-artifact@v3 - # with: - # name: test-build-log-${{ github.ref }}.html - # path: log.html - # - name: Cache-out outputs - # uses: actions/cache@v3 - # with: - # enableCrossOsArchive: true - # path: ${{ github.workspace }}/dist - # key: dist-${{ github.ref }}-${{ matrix.jabba_jdk }} - # test-cases-linux: - # strategy: - # matrix: - # jabba_jdk: [temurin@17, amazon-corretto@17, zulu@17, microsoft@17] - # target: [x86_64-unknown-linux-musl] - # runs-on: ubuntu-latest - # needs: build-on-linux - # steps: - # - uses: actions/checkout@v3 - # - name: Cache-in outputs - # uses: actions/cache@v3 - # with: - # enableCrossOsArchive: true - # path: ${{ github.workspace }}/dist - # dist- key: ${{ github.ref }}-${{ matrix.jabba_jdk }} - # - uses: actions/setup-python@v4 - # with: - # python-version: '3.10' - # - run: | - # cd ${{ github.workspace }} - # cp -r ${{ github.workspace }}/dist/x86_64-unknown-linux-musl/* . - # mkdir -p /opt/jdk - # curl -sL https://github.com/Jabba-Team/jabba/raw/main/install.sh | JABBA_COMMAND="install ${{ matrix.jabba_jdk }} -o /opt/jdk" bash - # export JAVA_HOME=/opt/jdk - # export PATH="$JAVA_HOME/bin:$PATH" - # python -m pip install robotframework - # python -m robot --exclude slow TestsBenchmark.robot - # test-cases-win: - # strategy: - # matrix: - # jabba_jdk: [temurin@17, amazon-corretto@17, zulu@17, microsoft@17] - # runs-on: windows-latest - # needs: build-on-linux - # steps: - # - uses: actions/checkout@v3 - # - uses: actions/setup-python@v4 - # with: - # python-version: '3.10' - # - name: Cache-in outputs - # uses: actions/cache@v3 - # with: - # enableCrossOsArchive: true - # path: ${{ github.workspace }}/dist - # dist- key: ${{ github.ref }}-${{ matrix.jabba_jdk }} - # - run: | - # cd ${{ github.workspace }} - # cp -r ${{ github.workspace }}/dist/x86_64-pc-windows-gnu/* . - # [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - # Invoke-Expression ( - # Invoke-WebRequest https://github.com/Jabba-Team/jabba/raw/master/install.ps1 -UseBasicParsing - # ).Content - # jabba install ${{ matrix.jabba_jdk }} - # jabba alias default ${{ matrix.jabba_jdk }} - # python -m pip install robotframework - # python -m robot --exclude slow TestsBenchmark.robot + report.html \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index dd864c79..be825d72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,26 @@ [workspace] members = [ - "rust-core", - "rust-orchestration", - "rust-blueprints", - "rust-common", - "rust-bridge-matlab-simulink" + "rust-core", + "rust-orchestration", + "rust-blueprints", + "rust-common", + "rust-bridge-matlab-simulink", + "rust-bridge-java", + # "rust-bridge-ortools" ] description = "IDeSyDe Rust suite" +resolver = "2" [workspace.dependencies] -base64 = "0.21.5" +env_logger = "0.11.2" +base64 = "0.22.0" ciborium = "0.2.1" clap = { version = "4.2.1", features = ["derive"] } -derive_builder = "0.12.0" +cxx = "1.0" +cxx-build = "1.0" +derive_builder = "0.20.0" downcast-rs = "1.2.0" -env_logger = "0.10.0" +jni = { version = "0.21.1", features = ["invocation"] } log = "0.4.17" md5 = "0.7.0" num = "0.4.1" @@ -23,8 +29,12 @@ prost = "0.12" prost-build = "0.12" quote = "1.0.27" rayon = "1.7" -reqwest = { version = "0.11.18", default-features = false, features = ["blocking", "rustls-tls", "json", "multipart"] } -reqwest-eventsource = "0.5.0" +reqwest = { version = "0.11.18", default-features = false, features = [ + "blocking", + "rustls-tls", + "json", + "multipart", +] } rmp-serde = "1.1" schemars = "0.8.12" serde = { version = "1.0", features = ["derive"] } @@ -32,12 +42,12 @@ serde_json = "1.0" sha2 = "0.10.8" sha3 = "0.10.6" syn = "2.0.15" -tungstenite = {version = "0.20.0", features = ["rustls"]} +tungstenite = { version = "0.21.0", features = ["rustls"] } url = "2.4.1" - - +rusqlite = { version = "0.31.0", features = ["bundled", "blob", "functions"] } +zip = "0.6.6" [workspace.package] -version = "0.6.2" +version = "0.8.0" authors = ["Rodolfo Jordao"] -edition = "2021" \ No newline at end of file +edition = "2021" diff --git a/TestsBenchmark.robot b/TestsBenchmark.robot index 4a2c9f91..f4787cad 100644 --- a/TestsBenchmark.robot +++ b/TestsBenchmark.robot @@ -155,10 +155,10 @@ Test for examples_and_benchmarks/PANORAMA/flight-information-function # ${NumFound} = IDeSyDeLibrary.Try Explore examples_and_benchmarks/PANORAMA/radar-aesa-function # Should Be Equal As Integers ${NumFound} 0 -Test for examples_and_benchmarks/small_and_explainable/sobel_and_2core_devicetree - ${NumFound} = IDeSyDeLibrary.Try Explore - ... examples_and_benchmarks/small_and_explainable/sobel_and_2core_devicetree - Should Not Be Equal As Integers ${NumFound} 0 +# Test for examples_and_benchmarks/small_and_explainable/sobel_and_2core_devicetree + # ${NumFound} = IDeSyDeLibrary.Try Explore + # ... examples_and_benchmarks/small_and_explainable/sobel_and_2core_devicetree + # Should Not Be Equal As Integers ${NumFound} 0 Test for examples_and_benchmarks/small_and_explainable/sobel_to_bus_multicore ${NumFound} = IDeSyDeLibrary.Try Explore diff --git a/build.sbt b/build.sbt index 2c728823..03997a2e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ -maintainer := "jordao@kth.se" +// maintainer := "jordao@kth.se" organization := "io.forsyde.github" -ThisBuild / scalaVersion := "3.3.0" +ThisBuild / scalaVersion := "3.4.0" ThisBuild / versionScheme := Some("early-semver") ThisBuild / publishMavenStyle := true ThisBuild / publishTo := Some(Opts.resolver.sonatypeStaging) @@ -96,8 +96,8 @@ lazy val common = (project in file("scala-common")) // .dependsOn(core) // .dependsOn(blueprints) // .enablePlugins(ScalaNativePlugin) - .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) - .enablePlugins(JDKPackagerPlugin) + // .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) + // .enablePlugins(JDKPackagerPlugin) // .enablePlugins(GraalVMNativeImagePlugin) .settings( // name := "idesyde-scala-common", @@ -115,10 +115,10 @@ lazy val common = (project in file("scala-common")) "MIT" -> url("https://opensource.org/license/mit/"), "APL2" -> url("https://www.apache.org/licenses/LICENSE-2.0") ), - jlinkIgnoreMissingDependency := JlinkIgnore.byPackagePrefix( - "scala.quoted" -> "scala", - "scalax.collection.generator" -> "org.scalacheck" - ) + // jlinkIgnoreMissingDependency := JlinkIgnore.byPackagePrefix( + // "scala.quoted" -> "scala", + // "scalax.collection.generator" -> "org.scalacheck" + // ) ) lazy val scala_legacy = (project in file("scala-bridge-forsyde-io")) @@ -127,8 +127,8 @@ lazy val scala_legacy = (project in file("scala-bridge-forsyde-io")) .dependsOn(devicetree) .dependsOn(choco) // .dependsOn(blueprints) - .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) - .enablePlugins(JDKPackagerPlugin) + // .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) + // .enablePlugins(JDKPackagerPlugin) // .enablePlugins(GraalVMNativeImagePlugin) .settings( // name := "idesyde-scala-bridge-forsyde-io", @@ -148,32 +148,32 @@ lazy val scala_legacy = (project in file("scala-bridge-forsyde-io")) "APL2" -> url("https://www.apache.org/licenses/LICENSE-2.0"), "EPL2" -> url("https://www.eclipse.org/legal/epl-2.0/") ), - jlinkModulePath := { - val paths = (jlinkBuildImage / fullClasspath).value - paths - .filter(f => { - f.get(moduleID.key) - .exists(mID => - mID.name.contains("jheaps") || - mID.name.contains("antlr4") || - mID.name.contains("automaton") || - mID.name.contains("xchart") || - mID.name == "commons-lang3" || - mID.name.contains("trove4j") - ) - // f.get(moduleID.key).exists(mID => mID.name.contains("amalthea")) || - // f.get(moduleID.key).exists(mID => mID.name.contains("emf")) || - // f.get(moduleID.key).exists(mID => mID.name.contains("lang3")) - }) - .map(_.data) - }, - jlinkIgnoreMissingDependency := JlinkIgnore.byPackagePrefix( - "scala.quoted" -> "scala", - "scalax.collection.generator" -> "org.scalacheck", - "org.glassfish.jaxb.runtime.v2.runtime" -> "com.sun.xml", - "org.glassfish.jaxb.runtime.v2.runtime" -> "org.jvnet", - "org.antlr.runtime" -> "org.antlr.stringtemplate" - ) + // jlinkModulePath := { + // val paths = (jlinkBuildImage / fullClasspath).value + // paths + // .filter(f => { + // f.get(moduleID.key) + // .exists(mID => + // mID.name.contains("jheaps") || + // mID.name.contains("antlr4") || + // mID.name.contains("automaton") || + // mID.name.contains("xchart") || + // mID.name == "commons-lang3" || + // mID.name.contains("trove4j") + // ) + // // f.get(moduleID.key).exists(mID => mID.name.contains("amalthea")) || + // // f.get(moduleID.key).exists(mID => mID.name.contains("emf")) || + // // f.get(moduleID.key).exists(mID => mID.name.contains("lang3")) + // }) + // .map(_.data) + // }, + // jlinkIgnoreMissingDependency := JlinkIgnore.byPackagePrefix( + // "scala.quoted" -> "scala", + // "scalax.collection.generator" -> "org.scalacheck", + // "org.glassfish.jaxb.runtime.v2.runtime" -> "com.sun.xml", + // "org.glassfish.jaxb.runtime.v2.runtime" -> "org.jvnet", + // "org.antlr.runtime" -> "org.antlr.stringtemplate" + // ) ) // lazy val minizinc = (project in file("scala-minizinc")) @@ -197,8 +197,8 @@ lazy val choco = (project in file("scala-choco")) .dependsOn(common) // .dependsOn(forsyde) // .dependsOn(blueprints) - .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) - .enablePlugins(JDKPackagerPlugin) + // .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) + // .enablePlugins(JDKPackagerPlugin) // .enablePlugins(GraalVMNativeImagePlugin) .settings( // name := "idesyde-scala-choco", @@ -215,32 +215,32 @@ lazy val choco = (project in file("scala-choco")) ), Compile / mainClass := Some("idesyde.choco.ChocoExplorationModule"), // moduleSettings, - jlinkModulePath := { - val paths = (jlinkBuildImage / fullClasspath).value - paths - .filter(f => { - f.get(moduleID.key).exists(mID => mID.name.contains("jheaps")) || - // f.get(moduleID.key).exists(mID => mID.name.contains("fastutil")) || - // f.get(moduleID.key).exists(mID => mID.name.contains("commons-text")) || - f.get(moduleID.key).exists(mID => mID.name.contains("antlr4")) || - f.get(moduleID.key).exists(mID => mID.name.contains("automaton")) || - f.get(moduleID.key).exists(mID => mID.name.contains("xchart")) || - f.get(moduleID.key).exists(mID => mID.name.contains("trove4j")) - }) - .map(_.data) - }, - graalVMNativeImageOptions := Seq("--no-fallback", "-H:+ReportExceptionStackTraces"), - jlinkIgnoreMissingDependency := JlinkIgnore.byPackagePrefix( - "scala.quoted" -> "scala", - "scalax.collection.generator" -> "org.scalacheck", - "org.glassfish.jaxb.runtime.v2.runtime" -> "com.sun.xml", - "org.glassfish.jaxb.runtime.v2.runtime" -> "org.jvnet", - "org.antlr.runtime" -> "org.antlr.stringtemplate", - "org.knowm.xchart" -> "org.apache.pdfbox", - "org.knowm.xchart" -> "de.rototor", - "org.knowm.xchart" -> "de.erichseifert", - "org.knowm.xchart" -> "com.madgag" - ) + // jlinkModulePath := { + // val paths = (jlinkBuildImage / fullClasspath).value + // paths + // .filter(f => { + // f.get(moduleID.key).exists(mID => mID.name.contains("jheaps")) || + // // f.get(moduleID.key).exists(mID => mID.name.contains("fastutil")) || + // // f.get(moduleID.key).exists(mID => mID.name.contains("commons-text")) || + // f.get(moduleID.key).exists(mID => mID.name.contains("antlr4")) || + // f.get(moduleID.key).exists(mID => mID.name.contains("automaton")) || + // f.get(moduleID.key).exists(mID => mID.name.contains("xchart")) || + // f.get(moduleID.key).exists(mID => mID.name.contains("trove4j")) + // }) + // .map(_.data) + // }, + // graalVMNativeImageOptions := Seq("--no-fallback", "-H:+ReportExceptionStackTraces"), + // jlinkIgnoreMissingDependency := JlinkIgnore.byPackagePrefix( + // "scala.quoted" -> "scala", + // "scalax.collection.generator" -> "org.scalacheck", + // "org.glassfish.jaxb.runtime.v2.runtime" -> "com.sun.xml", + // "org.glassfish.jaxb.runtime.v2.runtime" -> "org.jvnet", + // "org.antlr.runtime" -> "org.antlr.stringtemplate", + // "org.knowm.xchart" -> "org.apache.pdfbox", + // "org.knowm.xchart" -> "de.rototor", + // "org.knowm.xchart" -> "de.erichseifert", + // "org.knowm.xchart" -> "com.madgag" + // ) ) // lazy val matlab = (project in file("scala-bridge-matlab")) @@ -273,8 +273,8 @@ lazy val devicetree = (project in file("scala-bridge-device-tree")) // .dependsOn(core) .dependsOn(common) // .dependsOn(blueprints) - .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) - .enablePlugins(JDKPackagerPlugin) + // .enablePlugins(UniversalPlugin, JavaAppPackaging, JlinkPlugin) + // .enablePlugins(JDKPackagerPlugin) .settings( // name := "idesyde-scala-bridge-device-tree", libraryDependencies ++= Seq( @@ -349,6 +349,8 @@ ThisBuild / assembly / assemblyMergeStrategy := { (xs map { _.toLowerCase }) match { case "services" :: xs => MergeStrategy.filterDistinctLines + case "idesyde" :: xs => + MergeStrategy.filterDistinctLines case _ => MergeStrategy.discard } case x => MergeStrategy.first diff --git a/buildSrc/src/main/groovy/idesyde.java-standalone-module.gradle b/buildSrc/src/main/groovy/idesyde.java-standalone-module.gradle index e2eb47a6..0f372fea 100644 --- a/buildSrc/src/main/groovy/idesyde.java-standalone-module.gradle +++ b/buildSrc/src/main/groovy/idesyde.java-standalone-module.gradle @@ -24,6 +24,12 @@ graalvmNative { } } +dependencies { + implementation project(":java-core") + compileOnly project(":java-core-generator") + annotationProcessor project(":java-core-generator") +} + task publishModules(type: Copy) { dependsOn tasks.shadowJar from tasks.shadowJar.outputs diff --git a/java-blueprints/src/main/java/idesyde/blueprints/StandaloneModule.java b/java-blueprints/src/main/java/idesyde/blueprints/StandaloneModule.java index 0e9bc42b..c304ce5b 100644 --- a/java-blueprints/src/main/java/idesyde/blueprints/StandaloneModule.java +++ b/java-blueprints/src/main/java/idesyde/blueprints/StandaloneModule.java @@ -98,10 +98,13 @@ default Optional standaloneModule(String[] args) { .get("/decision/cache/exists", ctx -> { if (ctx.isMultipartFormData()) { - if (cachedDecisionModels.values().stream().anyMatch(m -> m.category().equals(ctx.formParam("category")))) { - var parts = cachedDecisionModels.values().stream().map(DecisionModel::part).collect(Collectors.toSet()); + if (cachedDecisionModels.values().stream() + .anyMatch(m -> m.category().equals(ctx.formParam("category")))) { + var parts = cachedDecisionModels.values().stream().map(DecisionModel::part) + .collect(Collectors.toSet()); for (var e : ctx.formParams("part")) { - // System.out.println("Checking if " + e + " is in parts for category " + ctx.formParam("category")); + // System.out.println("Checking if " + e + " is in parts for category " + + // ctx.formParam("category")); if (parts.stream().noneMatch(s -> s.contains(e))) { ctx.result("false"); return; @@ -114,21 +117,23 @@ default Optional standaloneModule(String[] args) { } else { var bb = ByteBuffer.wrap(ctx.bodyAsBytes()); if (cachedDecisionModels.containsKey(bb)) { - // System.out.println("YES decision cache exists of " - // + Arrays.toString(ctx.bodyAsBytes())); + // System.out.println("YES decision cache exists of " + // + Arrays.toString(ctx.bodyAsBytes())); ctx.result("true"); } else { - // System.out.println("NO decision cache exists of " - // + Arrays.toString(ctx.bodyAsBytes())); + // System.out.println("NO decision cache exists of " + // + Arrays.toString(ctx.bodyAsBytes())); ctx.result("false"); } - } + } }) .get("/design/cache/exists", ctx -> { if (ctx.isMultipartFormData()) { - if (cachedDesignModels.values().stream().anyMatch(m -> m.category().equals(ctx.formParam("category")))) { - var elements = cachedDesignModels.values().stream().map(DesignModel::elements).collect(Collectors.toSet()); + if (cachedDesignModels.values().stream() + .anyMatch(m -> m.category().equals(ctx.formParam("category")))) { + var elements = cachedDesignModels.values().stream().map(DesignModel::elements) + .collect(Collectors.toSet()); for (var e : ctx.formParams("elements")) { if (elements.stream().noneMatch(s -> s.contains(e))) { ctx.result("false"); @@ -151,8 +156,10 @@ default Optional standaloneModule(String[] args) { .get("/solved/cache/exists", ctx -> { if (ctx.isMultipartFormData()) { - if (cachedSolvedDecisionModels.values().stream().anyMatch(m -> m.category().equals(ctx.formParam("category")))) { - var elements = cachedSolvedDecisionModels.values().stream().map(DecisionModel::part).collect(Collectors.toSet()); + if (cachedSolvedDecisionModels.values().stream() + .anyMatch(m -> m.category().equals(ctx.formParam("category")))) { + var elements = cachedSolvedDecisionModels.values().stream() + .map(DecisionModel::part).collect(Collectors.toSet()); for (var e : ctx.formParams("part")) { if (elements.stream().noneMatch(s -> s.contains(e))) { ctx.result("false"); @@ -175,15 +182,16 @@ default Optional standaloneModule(String[] args) { .get("/decision/cache/fetch", ctx -> { var bb = ByteBuffer.wrap(ctx.bodyAsBytes()); // cachedDecisionModels.stream() - // .filter(m -> m.globalSHA2Hash().map(hash -> Arrays.equals(hash, ctx.bodyAsBytes())) - // .orElse(false)) - // .findAny() - // .map(OpaqueDecisionModel::from) - // .flatMap(OpaqueDecisionModel::toJsonString) - // .ifPresentOrElse(ctx::result, () -> ctx.result("Not in cache")); + // .filter(m -> m.globalSHA2Hash().map(hash -> Arrays.equals(hash, + // ctx.bodyAsBytes())) + // .orElse(false)) + // .findAny() + // .map(OpaqueDecisionModel::from) + // .flatMap(OpaqueDecisionModel::toJsonString) + // .ifPresentOrElse(ctx::result, () -> ctx.result("Not in cache")); if (cachedDecisionModels.containsKey(bb)) { OpaqueDecisionModel.from(cachedDecisionModels.get(bb)).toJsonString() - .ifPresentOrElse(ctx::result, () -> ctx.result("Not in cache")); + .ifPresentOrElse(ctx::result, () -> ctx.result("Not in cache")); } else { ctx.result("Not in cache"); ctx.status(404); @@ -193,7 +201,7 @@ default Optional standaloneModule(String[] args) { var bb = ByteBuffer.wrap(ctx.bodyAsBytes()); if (cachedDesignModels.containsKey(bb)) { OpaqueDesignModel.from(cachedDesignModels.get(bb)).toJsonString() - .ifPresentOrElse(ctx::result, () -> ctx.result("Not in cache")); + .ifPresentOrElse(ctx::result, () -> ctx.result("Not in cache")); } else { ctx.status(404); } @@ -217,45 +225,66 @@ default Optional standaloneModule(String[] args) { ctx.status(404); } }) - .put("/decision/cache/add", + .post("/decision/cache/add", ctx -> { // System.out.println("Adding to decision cache: " + ctx.body()); - OpaqueDecisionModel.fromJsonString(ctx.body()).ifPresentOrElse(opaque -> { - var bb = ByteBuffer.wrap(opaque.globalSHA2Hash().get()); // TODO: fix possibl NPE later - fromOpaqueDecision(opaque).ifPresentOrElse(m -> { - // System.out.println("Adding non-opaque to decision cache: " - // + m.globalSHA2Hash().map(Arrays::toString).orElse("NO HASH")); - cachedDecisionModels.put(bb, m); - }, () -> { - // System.out.println("Adding opaque to decision cache: " - // + opaque.globalSHA2Hash().map(Arrays::toString).orElse("NO HASH")); - cachedDecisionModels.put(bb, opaque); + if (ctx.isMultipartFormData()) { + OpaqueDecisionModel.fromJsonString(ctx.formParam("decisionModel")) + .ifPresentOrElse(opaque -> { + var bb = ByteBuffer.wrap(opaque.globalSHA2Hash().get()); // TODO: fix + // possibl NPE + // later + fromOpaqueDecision(opaque).ifPresentOrElse(m -> { + // System.out.println("Adding non-opaque to decision cache: " + // + m.globalSHA2Hash().map(Arrays::toString).orElse("NO HASH")); + cachedDecisionModels.put(bb, m); + }, () -> { + // System.out.println("Adding opaque to decision cache: " + // + opaque.globalSHA2Hash().map(Arrays::toString).orElse("NO + // HASH")); + cachedDecisionModels.put(bb, opaque); + }); + ctx.status(200); + ctx.result(opaque.globalSHA2Hash().map(Arrays::toString) + .orElse("NO HASH")); + // opaque.globalSHA2Hash().ifPresent(hash -> cachedDecisionModels + // .put(ByteBuffer.wrap(hash), fromOpaqueDecision(opaque))); + }, () -> ctx.status(500)); + } + }) + .post("/solved/cache/add", + ctx -> { + if (ctx.isMultipartFormData()) { + OpaqueDecisionModel.fromJsonString(ctx.formParam("solvedModel")) + .flatMap(this::fromOpaqueDecision) + .ifPresent(m -> m.globalSHA2Hash().ifPresent( + hash -> cachedSolvedDecisionModels.put(ByteBuffer.wrap(hash), m))); + } + }) + .post("/design/cache/add", + ctx -> { + if (ctx.isMultipartFormData()) { + OpaqueDesignModel.fromJsonString(ctx.formParam("designModel")).ifPresent(opaque -> { + fromOpaqueDesign(opaque).ifPresentOrElse(m -> { + // System.out.println("Adding non opaque design model to cache: " + + // m.category()); + cachedDesignModels.put(ByteBuffer.wrap(opaque.globalSHA2Hash().get()), m); + }, () -> cachedDesignModels.put(ByteBuffer.wrap(opaque.globalSHA2Hash().get()), + opaque)); }); ctx.status(200); - ctx.result(opaque.globalSHA2Hash().map(Arrays::toString).orElse("NO HASH")); - // opaque.globalSHA2Hash().ifPresent(hash -> cachedDecisionModels - // .put(ByteBuffer.wrap(hash), fromOpaqueDecision(opaque))); - }, () -> ctx.status(500)); + ctx.result("OK"); + } }) - .put("/solved/cache/add", - ctx -> OpaqueDecisionModel.fromJsonString(ctx.body()).flatMap(this::fromOpaqueDecision) - .ifPresent(m -> m.globalSHA2Hash().ifPresent( - hash -> cachedSolvedDecisionModels.put(ByteBuffer.wrap(hash), m)))) - .put("/design/cache/add", + .post("/reversed/cache/add", ctx -> { - OpaqueDesignModel.fromJsonString(ctx.body()).ifPresent(opaque -> { - fromOpaqueDesign(opaque).ifPresentOrElse(m -> { - // System.out.println("Adding non opaque design model to cache: " + m.category()); - cachedDesignModels.put(ByteBuffer.wrap(opaque.globalSHA2Hash().get()), m); - }, () -> cachedDesignModels.put(ByteBuffer.wrap(opaque.globalSHA2Hash().get()), opaque)); - }); - ctx.status(200); - ctx.result("OK"); + if (ctx.isMultipartFormData()) { + OpaqueDesignModel.fromJsonString(ctx.formParam("reversedModel")) + .flatMap(this::fromOpaqueDesign) + .ifPresent(m -> m.globalSHA2Hash().ifPresent( + hash -> cachedReversedDesignModels.put(ByteBuffer.wrap(hash), m))); + } }) - .put("/reversed/cache/add", - ctx -> OpaqueDesignModel.fromJsonString(ctx.body()).flatMap(this::fromOpaqueDesign) - .ifPresent(m -> m.globalSHA2Hash().ifPresent( - hash -> cachedReversedDesignModels.put(ByteBuffer.wrap(hash), m)))) .post("/decision/cache/clear", ctx -> cachedDecisionModels.clear()) .post("/design/cache/clear", ctx -> cachedDesignModels.clear()) .post("/solved/cache/clear", ctx -> cachedSolvedDecisionModels.clear()) @@ -348,7 +377,8 @@ default Optional standaloneModule(String[] args) { var results = identification(designModels, decisionModels); for (var result : results.identified()) { result.globalSHA2Hash().ifPresent(hash -> { - // System.out.println("Adding a %s decision model with hash %s to cache".formatted(result.category(), Arrays.toString(hash))); + // System.out.println("Adding a %s decision model with hash %s to + // cache".formatted(result.category(), Arrays.toString(hash))); cachedDecisionModels.put(ByteBuffer.wrap(hash), result); }); } @@ -425,8 +455,7 @@ default Optional standaloneModule(String[] args) { entries.stream().findAny().ifPresent(msg -> { OpaqueDecisionModel.fromJsonString(msg) .flatMap(this::fromOpaqueDecision) - .map(decisionModel -> explorer.bid(explorers(), - decisionModel)) + .map(decisionModel -> explorer.bid(decisionModel)) .ifPresent(bid -> { try { ctx.result(objectMapper.writeValueAsString(bid)); @@ -441,10 +470,10 @@ default Optional standaloneModule(String[] args) { } else { var bb = ByteBuffer.wrap(ctx.bodyAsBytes()); // System.out.println( - // "Bidding with %s and %s".formatted(Arrays.toString(ctx.bodyAsBytes()), - // explorer.uniqueIdentifier())); + // "Bidding with %s and %s".formatted(Arrays.toString(ctx.bodyAsBytes()), + // explorer.uniqueIdentifier())); var decisionModel = cachedDecisionModels.get(bb); - var bid = explorer.bid(explorers(), decisionModel); + var bid = explorer.bid(decisionModel); try { // System.out.println("returning bidding value"); ctx.result(objectMapper.writeValueAsString(bid)); @@ -673,14 +702,15 @@ default Optional standaloneModule(String[] args) { }) .updateConfig(config -> { config.jetty.multipartConfig.maxTotalRequestSize(1, SizeUnit.GB); + config.jetty.multipartConfig.maxFileSize(1, SizeUnit.GB); config.jetty.wsFactoryConfig(cfg -> { - cfg.setMaxTextMessageSize(100000000); - cfg.setMaxBinaryMessageSize(100000000); + cfg.setMaxTextMessageSize(1000000000); + cfg.setMaxBinaryMessageSize(1000000000); }); config.jetty.contextHandlerConfig(ctx -> { - ctx.setMaxFormContentSize(100000000); + ctx.setMaxFormContentSize(1000000000); }); - config.http.maxRequestSize = 100000000; + config.http.maxRequestSize = 1000000000; }); server.events(es -> { es.serverStarted(() -> { diff --git a/java-bridge-forsyde-io/build.gradle b/java-bridge-forsyde-io/build.gradle index 31c91424..e5b60508 100644 --- a/java-bridge-forsyde-io/build.gradle +++ b/java-bridge-forsyde-io/build.gradle @@ -16,6 +16,8 @@ dependencies { implementation project(":java-core") implementation project(":java-common") implementation project(":java-blueprints") + compileOnly project(":java-core-generator") + annotationProcessor project(":java-core-generator") } test { diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticoreReverseIdentification.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticoreReverseIdentification.java index f7fb92d8..e2966947 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticoreReverseIdentification.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticoreReverseIdentification.java @@ -1,6 +1,7 @@ package idesyde.forsydeio; import idesyde.common.AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore; +import idesyde.core.AutoRegister; import idesyde.core.DecisionModel; import idesyde.core.DesignModel; import idesyde.core.ReverseIdentificationRule; @@ -10,7 +11,7 @@ import forsyde.io.core.SystemGraph; import forsyde.io.lib.hierarchy.ForSyDeHierarchy; - +@AutoRegister(ForSyDeIOModule.class) public class AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticoreReverseIdentification implements ReverseIdentificationRule { diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedTiledMulticoreReverseIdentification.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedTiledMulticoreReverseIdentification.java index d56c6bb0..85f6bdd1 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedTiledMulticoreReverseIdentification.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/AperiodicAsynchronousDataflowToPartitionedTiledMulticoreReverseIdentification.java @@ -1,6 +1,7 @@ package idesyde.forsydeio; import idesyde.common.AperiodicAsynchronousDataflowToPartitionedTiledMulticore; +import idesyde.core.AutoRegister; import idesyde.core.DecisionModel; import idesyde.core.DesignModel; import idesyde.core.ReverseIdentificationRule; @@ -10,7 +11,7 @@ import forsyde.io.core.SystemGraph; import forsyde.io.lib.hierarchy.ForSyDeHierarchy; - +@AutoRegister(ForSyDeIOModule.class) public class AperiodicAsynchronousDataflowToPartitionedTiledMulticoreReverseIdentification implements ReverseIdentificationRule { diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIODesignModel.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIODesignModel.java index 05fd9af4..e411e2f1 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIODesignModel.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIODesignModel.java @@ -8,6 +8,7 @@ import forsyde.io.lib.hierarchy.ForSyDeHierarchy; import forsyde.io.lib.LibForSyDeModelHandler; import idesyde.core.DesignModel; +import idesyde.core.OpaqueDesignModel; import java.util.Optional; import java.util.Set; @@ -15,14 +16,15 @@ import java.util.stream.Stream; /** - * This design model wraps ForSyDe IO system graphs in order to make it usable in the DSI conceptual framework + * This design model wraps ForSyDe IO system graphs in order to make it usable + * in the DSI conceptual framework * and IDeSyDe framework. + * * @param systemGraph the ForSyDe IO system graph wrapped. */ public record ForSyDeIODesignModel( SystemGraph systemGraph) implements DesignModel { - @Override public Optional asString() { try { @@ -38,6 +40,39 @@ public String format() { return "fiodl"; } + @Override + public Set elements() { + return systemGraph.vertexSet().stream().map(Vertex::getIdentifier).collect(Collectors.toSet()); + // return + // Stream.concat(systemGraph.vertexSet().stream().map(Vertex::getIdentifier), + // systemGraph().edgeSet().stream().map(EdgeInfo::toIDString)).collect(Collectors.toSet()); + } + + public static Optional fromOpaque(OpaqueDesignModel opaque) { + if (modelHandler.canLoadModel(opaque.format())) { + return opaque.asString().flatMap(body -> { + try { + return Optional.of(modelHandler.readModel(body, opaque.format())); + } catch (Exception e) { + e.printStackTrace(); + return Optional.empty(); + } + }).map(ForSyDeIODesignModel::new); + } else { + return Optional.empty(); + } + } + + public static Optional tryFrom(DesignModel model) { + if (model instanceof OpaqueDesignModel opaque) { + return fromOpaque(opaque); + } else if (model instanceof ForSyDeIODesignModel forSyDeIODesignModel) { + return Optional.of(forSyDeIODesignModel); + } else { + return Optional.empty(); + } + } + public static ModelHandler modelHandler = LibForSyDeModelHandler.registerLibForSyDe(new ModelHandler()) .registerDriver(new SDF3Driver()); } diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOModule.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOModule.java index 78e65a5a..f5881888 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOModule.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOModule.java @@ -9,12 +9,11 @@ import java.util.Optional; import java.util.Set; -public class ForSyDeIOModule implements StandaloneModule { - - ModelHandler modelHandler = ForSyDeIODesignModel.modelHandler; +public interface ForSyDeIOModule extends StandaloneModule { @Override - public Optional fromOpaqueDesign(OpaqueDesignModel opaque) { + default Optional fromOpaqueDesign(OpaqueDesignModel opaque) { + ModelHandler modelHandler = ForSyDeIODesignModel.modelHandler; if (modelHandler.canLoadModel(opaque.format())) { return opaque.asString().flatMap(body -> { try { @@ -27,23 +26,10 @@ public Optional fromOpaqueDesign(OpaqueDesignModel opaque) { } else { return Optional.empty(); } - // var pathOpt = opaque.format().modelPaths().stream().map(x -> Paths.get(x)) - // .filter(x -> modelHandler.canLoadModel(x)).findAny(); - // var extIdxOpt = pathOpt.map(x -> x.getFileName().toString().indexOf(".")); - // var extOpt = extIdxOpt.flatMap(x -> pathOpt.map(p -> - // p.getFileName().toString().substring(x + 1))); - // return opaque.body().flatMap(b -> extOpt.flatMap(ext -> { - // try { - // return Optional.of(new ForSyDeIODesignModel(modelHandler.readModel(b, ext))); - // } catch (Exception e) { - // e.printStackTrace(); - // return Optional.empty(); - // } - // })); } @Override - public Optional fromOpaqueDecision(OpaqueDecisionModel opaque) { + default Optional fromOpaqueDecision(OpaqueDecisionModel opaque) { return switch (opaque.category()) { case "AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore" -> opaque.asCBORBinary().flatMap(b -> readFromCBORBytes(b, @@ -62,32 +48,4 @@ public Optional fromOpaqueDecision(OpaqueDecisionModel opaque) { default -> Optional.empty(); }; } - - @Override - public String uniqueIdentifier() { - return "ForSyDeIOJavaModule"; - } - - @Override - public Set reverseIdentificationRules() { - return Set.of( - new AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticoreReverseIdentification(), - new AperiodicAsynchronousDataflowToPartitionedTiledMulticoreReverseIdentification()); - } - - @Override - public Set identificationRules() { - return Set.of( - new MemoryMappableMultiCoreIRule(), - new ForSyDeIOSYNetworkToAADataflowIRule(), - new ForSyDeIOSYAndSDFInstrumentedToMemReqIRule(), - new TiledMultiCoreIRule(), - new InstrumentedComputationTimesIRule(), - new ForSyDeIOSDFToCommon()); - } - - public static void main(String[] args) { - var server = new ForSyDeIOModule().standaloneModule(args); - server.ifPresent(s -> s.start(0)); - } } diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSDFToCommon.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSDFToCommon.java index b6164b6c..80d99450 100755 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSDFToCommon.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSDFToCommon.java @@ -14,11 +14,9 @@ import forsyde.io.lib.hierarchy.behavior.moc.sdf.SDFActor; import forsyde.io.lib.hierarchy.behavior.moc.sdf.SDFChannel; import idesyde.common.SDFApplication; -import idesyde.core.DecisionModel; -import idesyde.core.DesignModel; -import idesyde.core.IdentificationResult; -import idesyde.core.IdentificationRule; +import idesyde.core.*; +@AutoRegister(ForSyDeIOModule.class) class ForSyDeIOSDFToCommon implements IdentificationRule { @Override diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYAndSDFInstrumentedToMemReqIRule.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYAndSDFInstrumentedToMemReqIRule.java index 68d57ef3..b28ba48e 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYAndSDFInstrumentedToMemReqIRule.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYAndSDFInstrumentedToMemReqIRule.java @@ -9,21 +9,17 @@ import forsyde.io.core.SystemGraph; import forsyde.io.lib.hierarchy.ForSyDeHierarchy; import idesyde.common.InstrumentedMemoryRequirements; -import idesyde.core.DecisionModel; -import idesyde.core.DesignModel; -import idesyde.core.IdentificationResult; -import idesyde.core.IdentificationRule; +import idesyde.core.*; -class ForSyDeIOSYAndSDFInstrumentedToMemReqIRule implements IdentificationRule { +@AutoRegister(ForSyDeIOModule.class) +public class ForSyDeIOSYAndSDFInstrumentedToMemReqIRule implements IdentificationRule { @Override public IdentificationResult apply(Set designModels, Set decisionModels) { var model = new SystemGraph(); for (var dm : designModels) { - if (dm instanceof ForSyDeIODesignModel m) { - model.mergeInPlace(m.systemGraph()); - } + ForSyDeIODesignModel.tryFrom(dm).map(ForSyDeIODesignModel::systemGraph).ifPresent(model::mergeInPlace); } Set processes = new HashSet<>(); Set channels = new HashSet<>(); @@ -39,7 +35,7 @@ public IdentificationResult apply(Set designModels, memMapping.get(ib.getIdentifier()).put(inspe.getIdentifier(), ib.maxSizeInBits().entrySet().stream() .filter(e -> inspe.modalInstructionCategory().contains(e.getKey())) - .mapToLong(e -> e.getValue()).max() + .mapToLong(Map.Entry::getValue).max() .orElse(0L)); }, () -> { ForSyDeHierarchy.GenericProcessingModule.tryView(model, peV).ifPresent(pe -> { @@ -47,7 +43,7 @@ public IdentificationResult apply(Set designModels, memMapping.put(ib.getIdentifier(), new HashMap<>()); } memMapping.get(ib.getIdentifier()).put(pe.getIdentifier(), - ib.maxSizeInBits().values().stream().mapToLong(x -> x.longValue()).max() + ib.maxSizeInBits().values().stream().mapToLong(Long::longValue).max() .orElse(0L)); }); }); @@ -61,7 +57,7 @@ public IdentificationResult apply(Set designModels, memMapping.put(idt.getIdentifier(), new HashMap<>()); } memMapping.get(idt.getIdentifier()).put(pe.getIdentifier(), - idt.maxSizeInBits().values().stream().mapToLong(x -> x.longValue()).max().orElse(0L)); + idt.maxSizeInBits().values().stream().mapToLong(x -> x).max().orElse(0L)); }); } }); diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYNetworkToAADataflowIRule.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYNetworkToAADataflowIRule.java index fcbe60af..dd2f3d86 100755 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYNetworkToAADataflowIRule.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/ForSyDeIOSYNetworkToAADataflowIRule.java @@ -8,6 +8,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import idesyde.core.*; import org.jgrapht.alg.connectivity.ConnectivityInspector; import org.jgrapht.graph.AsSubgraph; @@ -17,11 +18,8 @@ import forsyde.io.lib.hierarchy.behavior.moc.sy.SYMap; import forsyde.io.lib.hierarchy.behavior.moc.sy.SYSignal; import idesyde.common.AperiodicAsynchronousDataflow; -import idesyde.core.DecisionModel; -import idesyde.core.DesignModel; -import idesyde.core.IdentificationResult; -import idesyde.core.IdentificationRule; +@AutoRegister(ForSyDeIOModule.class) class ForSyDeIOSYNetworkToAADataflowIRule implements IdentificationRule { @Override diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/InstrumentedComputationTimesIRule.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/InstrumentedComputationTimesIRule.java index 46f8ac26..18e01ed9 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/InstrumentedComputationTimesIRule.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/InstrumentedComputationTimesIRule.java @@ -8,11 +8,9 @@ import forsyde.io.core.SystemGraph; import forsyde.io.lib.hierarchy.ForSyDeHierarchy; import idesyde.common.InstrumentedComputationTimes; -import idesyde.core.DecisionModel; -import idesyde.core.DesignModel; -import idesyde.core.IdentificationResult; -import idesyde.core.IdentificationRule; +import idesyde.core.*; +@AutoRegister(ForSyDeIOModule.class) class InstrumentedComputationTimesIRule implements IdentificationRule { @Override public IdentificationResult apply(Set designModels, diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/MemoryMappableMultiCoreIRule.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/MemoryMappableMultiCoreIRule.java index 4b25a821..665982b4 100755 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/MemoryMappableMultiCoreIRule.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/MemoryMappableMultiCoreIRule.java @@ -7,6 +7,7 @@ import java.util.Set; import java.util.stream.Collectors; +import idesyde.core.*; import org.jgrapht.alg.connectivity.ConnectivityInspector; import org.jgrapht.alg.shortestpath.DijkstraManyToManyShortestPaths; import org.jgrapht.alg.shortestpath.FloydWarshallShortestPaths; @@ -19,11 +20,8 @@ import forsyde.io.lib.hierarchy.platform.hardware.GenericMemoryModule; import forsyde.io.lib.hierarchy.platform.hardware.GenericProcessingModule; import idesyde.common.MemoryMappableMultiCore; -import idesyde.core.DecisionModel; -import idesyde.core.DesignModel; -import idesyde.core.IdentificationResult; -import idesyde.core.IdentificationRule; +@AutoRegister(ForSyDeIOModule.class) class MemoryMappableMultiCoreIRule implements IdentificationRule { private record Pair(A fst, B snd) { diff --git a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/TiledMultiCoreIRule.java b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/TiledMultiCoreIRule.java index 42ba7e22..77cc4172 100644 --- a/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/TiledMultiCoreIRule.java +++ b/java-bridge-forsyde-io/src/main/java/idesyde/forsydeio/TiledMultiCoreIRule.java @@ -7,6 +7,7 @@ import java.util.Set; import java.util.stream.Collectors; +import idesyde.core.*; import org.jgrapht.alg.shortestpath.FloydWarshallShortestPaths; import org.jgrapht.graph.AsSubgraph; @@ -18,12 +19,9 @@ import forsyde.io.lib.hierarchy.platform.hardware.GenericProcessingModule; import idesyde.common.MemoryMappableMultiCore; import idesyde.common.TiledMultiCore; -import idesyde.core.DecisionModel; -import idesyde.core.DesignModel; -import idesyde.core.IdentificationResult; -import idesyde.core.IdentificationRule; -class TiledMultiCoreIRule implements IdentificationRule { +@AutoRegister(ForSyDeIOModule.class) +public class TiledMultiCoreIRule implements IdentificationRule { private record Pair(A fst, B snd) { }; diff --git a/java-bridge-forsyde-io/src/main/java/module-info.java b/java-bridge-forsyde-io/src/main/java/module-info.java index 93dc4815..0f877fcb 100644 --- a/java-bridge-forsyde-io/src/main/java/module-info.java +++ b/java-bridge-forsyde-io/src/main/java/module-info.java @@ -2,8 +2,11 @@ requires transitive idesyde.blueprints; requires transitive idesyde.common; + requires static idesyde.core.generator; requires transitive forsyde.io.core; requires transitive forsyde.io.libforsyde; requires transitive forsyde.io.java.sdfThree; + + exports idesyde.forsydeio; } \ No newline at end of file diff --git a/java-common/src/main/java/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.java b/java-common/src/main/java/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.java index 8999ea51..6c3b1d63 100644 --- a/java-common/src/main/java/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.java +++ b/java-common/src/main/java/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.java @@ -1,5 +1,6 @@ package idesyde.common; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import idesyde.core.DecisionModel; @@ -10,26 +11,26 @@ @JsonSerialize public record CommunicatingAndTriggeredReactiveWorkload( - List tasks, - List taskSizes, - List>> taskComputationalNeeds, - List dataChannels, - List dataChannelSizes, - List dataGraphSrc, - List dataGraphDst, - List dataGraphMessageSize, - List periodicSources, - List periods, - List offsets, - List upsamples, - List upsampleRepetitiveHolds, - List upsampleInitialHolds, - List downsamples, - List downampleRepetitiveSkips, - List downampleInitialSkips, - List triggerGraphSrc, - List triggerGraphDst, - Set hasORTriggerSemantics + @JsonProperty("tasks") List tasks, + @JsonProperty("task_sizes") List taskSizes, + @JsonProperty("task_computational_needs") List>> taskComputationalNeeds, + @JsonProperty("data_channels") List dataChannels, + @JsonProperty("data_channel_sizes") List dataChannelSizes, + @JsonProperty("data_graph_src") List dataGraphSrc, + @JsonProperty("data_graph_dst") List dataGraphDst, + @JsonProperty("data_graph_message_size") List dataGraphMessageSize, + @JsonProperty("periodic_sources") List periodicSources, + @JsonProperty("periods") List periods, + @JsonProperty("offsets") List offsets, + @JsonProperty("upsamples") List upsamples, + @JsonProperty("upsample_repetitive_holds") List upsampleRepetitiveHolds, + @JsonProperty("upsample_initial_holds") List upsampleInitialHolds, + @JsonProperty("downsamples") List downsamples, + @JsonProperty("downample_repetitive_skips") List downampleRepetitiveSkips, + @JsonProperty("downample_initial_skips") List downampleInitialSkips, + @JsonProperty("trigger_graph_src") List triggerGraphSrc, + @JsonProperty("trigger_graph_dst") List triggerGraphDst, + @JsonProperty("has_or_trigger_semantics") Set hasORTriggerSemantics ) implements DecisionModel { @Override diff --git a/java-core-generator/build.gradle b/java-core-generator/build.gradle new file mode 100644 index 00000000..8782dab0 --- /dev/null +++ b/java-core-generator/build.gradle @@ -0,0 +1,8 @@ +plugins { + id 'idesyde.java-library' +} + +dependencies { + implementation project(":java-core") + implementation 'com.squareup:javapoet:1.13.0' +} \ No newline at end of file diff --git a/java-core-generator/src/main/java/idesyde/core/generator/AutoModuleProcessor.java b/java-core-generator/src/main/java/idesyde/core/generator/AutoModuleProcessor.java new file mode 100644 index 00000000..c09ffe0b --- /dev/null +++ b/java-core-generator/src/main/java/idesyde/core/generator/AutoModuleProcessor.java @@ -0,0 +1,161 @@ +package idesyde.core.generator; + +import com.squareup.javapoet.*; +import idesyde.core.AutoRegister; +import idesyde.core.Explorer; +import idesyde.core.IdentificationRule; +import idesyde.core.Module; +import idesyde.core.ReverseIdentificationRule; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import javax.tools.StandardLocation; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SupportedAnnotationTypes({ "idesyde.core.AutoRegister" }) +@SupportedSourceVersion(SourceVersion.RELEASE_17) + +public class AutoModuleProcessor extends AbstractProcessor { + + protected Optional getValueOfAnnotationMirror(AnnotationMirror annotationMirror) { + return annotationMirror.getElementValues().entrySet().stream() + .filter(e -> e.getKey().getSimpleName().contentEquals("value")) + .flatMap(e -> processingEnv.getElementUtils().getAllTypeElements(e.getValue().getValue().toString()) + .stream()) + .findAny(); + } + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + var identificationRuleCls = processingEnv.getElementUtils().getTypeElement(IdentificationRule.class.getCanonicalName()); + var reverseIdentificationRuleCls = processingEnv.getElementUtils().getTypeElement(ReverseIdentificationRule.class.getCanonicalName()); + var explorerCls = processingEnv.getElementUtils().getTypeElement(Explorer.class.getCanonicalName()); + var elems = roundEnv.getElementsAnnotatedWith(AutoRegister.class); + HashMap irules = new HashMap<>(); + HashMap rrules = new HashMap<>(); + HashMap explorers = new HashMap<>(); + for (var elem : elems) { + if (elem.getKind().isClass()) { + elem.getAnnotationMirrors().stream().map(this::getValueOfAnnotationMirror).flatMap(Optional::stream).forEach(module -> { + var moduleElem = processingEnv.getElementUtils().getTypeElement(module.getQualifiedName()); + if (processingEnv.getTypeUtils().isAssignable(elem.asType(), identificationRuleCls.asType())) { // is IRule + if (elem instanceof TypeElement typeElement) { + irules.put(typeElement, moduleElem); + } + } + if (processingEnv.getTypeUtils().isAssignable(elem.asType(), reverseIdentificationRuleCls.asType())) { // is IRule + if (elem instanceof TypeElement typeElement) { + rrules.put(typeElement, moduleElem); + } + } + if (processingEnv.getTypeUtils().isAssignable(elem.asType(), explorerCls.asType())) { // is IRule + if (elem instanceof TypeElement typeElement) { + explorers.put(typeElement, moduleElem); + } + } + }); + } + } + var modules = Stream.concat(irules.values().stream(), Stream.concat(rrules.values().stream(), explorers.values().stream())) + .collect(Collectors.toSet()); + for (var module : modules) { + var autoModuleBuilder = TypeSpec.classBuilder("AutoModule" + module.getSimpleName()).addSuperinterface(ClassName.get(module.asType())).addModifiers(Modifier.FINAL, Modifier.PUBLIC); + var moduleIRules = irules.entrySet().stream().filter(e -> e.getValue().equals(module)).map(Map.Entry::getKey).collect(Collectors.toSet()); + var moduleRRules = rrules.entrySet().stream().filter(e -> e.getValue().equals(module)).map(Map.Entry::getKey).collect(Collectors.toSet()); + var moduleExplorers = explorers.entrySet().stream().filter(e -> e.getValue().equals(module)).map(Map.Entry::getKey).collect(Collectors.toSet()); + // add minimal stuff + autoModuleBuilder.addMethod( + MethodSpec.methodBuilder("uniqueIdentifier").returns(String.class).addModifiers(Modifier.PUBLIC).addStatement("return $S", module.getQualifiedName()).build() + ); + // add the methods for irules + autoModuleBuilder.addMethod( + MethodSpec.methodBuilder("identificationRules") + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get(Set.class, IdentificationRule.class)) + .addStatement("return Set.of(" + moduleIRules.stream().map(irule -> "new $T()").collect(Collectors.joining(", ")) + ")", moduleIRules.stream().map(irule -> ClassName.get(irule.asType())).toArray()) + .build() + ); + // add the methods for the orchestrator + autoModuleBuilder.addMethod( + MethodSpec.methodBuilder("identicationRulesCanonicalClassNames") + .addModifiers(Modifier.PUBLIC) + .returns(String[].class) + .addStatement("return new String[]{" + moduleIRules.stream().map(irule -> '"' + irule.getQualifiedName().toString() + '"').collect(Collectors.joining(", ")) + "}") + .build() + ); + // add the methods for rrules + autoModuleBuilder.addMethod( + MethodSpec.methodBuilder("reverseIdentificationRules") + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get(Set.class, ReverseIdentificationRule.class)) + .addStatement("return Set.of(" + moduleRRules.stream().map(irule -> "new $T()").collect(Collectors.joining(", ")) + ")", moduleRRules.stream().map(irule -> ClassName.get(irule.asType())).toArray()) + .build() + ); + // add the methods for the orchestrator (reverse) + autoModuleBuilder.addMethod( + MethodSpec.methodBuilder("reverseIdenticationRulesCanonicalClassNames") + .addModifiers(Modifier.PUBLIC) + .returns(String[].class) + .addStatement("return new String[]{" + moduleRRules.stream().map(irule -> '"' + irule.getQualifiedName().toString() + '"').collect(Collectors.joining(", ")) + "}") + .build() + ); + // finally the same for explorers + autoModuleBuilder.addMethod( + MethodSpec.methodBuilder("explorers") + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get(Set.class, Explorer.class)) + .addStatement("return Set.of(" + moduleExplorers.stream().map(irule -> "new $T()").collect(Collectors.joining(", ")) + ")", moduleExplorers.stream().map(irule -> ClassName.get(irule.asType())).toArray()) + .build() + ); + try { + var pak = processingEnv.getElementUtils().getPackageOf(module); + JavaFile.builder(pak.toString(), autoModuleBuilder.build()).build().writeTo(processingEnv.getFiler()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + var modulesForMetaINF = modules.stream().map(m -> processingEnv.getElementUtils().getPackageOf(m).toString() + ".AutoModule" + m.getSimpleName()).collect(Collectors.toCollection(HashSet::new)); + // try to get the resource file + try { + var metaINF = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/idesyde/automodules"); + try (var reader = new BufferedReader(new InputStreamReader(metaINF.openInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + modulesForMetaINF.remove(line); + } + } + + try (var writer = new BufferedWriter(new OutputStreamWriter(metaINF.openOutputStream(), StandardCharsets.UTF_8))) { + for (var m : modulesForMetaINF) { + writer.append(m).append('\n'); + } + } + } catch (IOException e) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.OTHER, "No IDeSyDe META-INF found. Creating one"); + } + // the only reason the modules still exist is if the file did not exist. We create it now. + if (!modulesForMetaINF.isEmpty()) { + try { + var metaINF = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/idesyde/automodules", modules.toArray(new TypeElement[0])); + try (var writer = new BufferedWriter(new OutputStreamWriter(metaINF.openOutputStream(), StandardCharsets.UTF_8))) { + for (var m : modulesForMetaINF) { + writer.append(m).append('\n'); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return false; + } +} diff --git a/java-core-generator/src/main/java/module-info.java b/java-core-generator/src/main/java/module-info.java new file mode 100644 index 00000000..d6c78f82 --- /dev/null +++ b/java-core-generator/src/main/java/module-info.java @@ -0,0 +1,8 @@ +module idesyde.core.generator { + requires java.base; + requires java.compiler; + + requires transitive idesyde.core; + requires com.squareup.javapoet; + exports idesyde.core.generator; +} \ No newline at end of file diff --git a/java-core-generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/java-core-generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000..44bc170a --- /dev/null +++ b/java-core-generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +idesyde.core.generator.AutoModuleProcessor \ No newline at end of file diff --git a/java-core/src/main/java/idesyde/core/AutoRegister.java b/java-core/src/main/java/idesyde/core/AutoRegister.java new file mode 100644 index 00000000..087347cc --- /dev/null +++ b/java-core/src/main/java/idesyde/core/AutoRegister.java @@ -0,0 +1,17 @@ +package idesyde.core; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation ensures that an (reverse) identification rule or an explorer can be found later by the + * orchestrator. + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface AutoRegister { + + Class value(); +} diff --git a/java-core/src/main/java/idesyde/core/DecisionModel.java b/java-core/src/main/java/idesyde/core/DecisionModel.java index 143f2bab..131e709c 100644 --- a/java-core/src/main/java/idesyde/core/DecisionModel.java +++ b/java-core/src/main/java/idesyde/core/DecisionModel.java @@ -1,10 +1,13 @@ package idesyde.core; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.exc.StreamReadException; +import com.fasterxml.jackson.databind.DatabindException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.cbor.CBORFactory; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -52,6 +55,10 @@ default Set part() { return Set.of(); } + default String[] partAsArray() { + return part().toArray(new String[0]); + } + /** * @return The category that describes this decision model. Default value (and * recommendation) is the class name. @@ -119,6 +126,41 @@ default int compareTo(DecisionModel o) { })).orElse(0); } + static Optional fromCBOR(byte[] bytes, Class cls) { + try { + return Optional.of(objectMapperCBOR.readValue(bytes, cls)); + } catch (IOException e) { + return Optional.empty(); + } + } + + static Optional fromJsonString(String str, Class cls) { + try { + return Optional.of(objectMapper.readValue(str, cls)); + } catch (IOException e) { + return Optional.empty(); + } + } + + public static Optional fromOpaque(OpaqueDecisionModel opaqueDecisionModel, + Class cls) { + if (opaqueDecisionModel.category().equals(cls.getName()) + || opaqueDecisionModel.category().equals(cls.getCanonicalName())) { + return opaqueDecisionModel.asCBORBinary().flatMap(bs -> DecisionModel.fromCBOR(bs, cls)).or( + () -> opaqueDecisionModel.asJsonString().flatMap(str -> DecisionModel.fromJsonString(str, cls))); + } + return Optional.empty(); + } + + @SuppressWarnings("unchecked") + public static Optional cast(DecisionModel m, Class cls) { + if (m instanceof OpaqueDecisionModel opaqueDecisionModel) { + return fromOpaque(opaqueDecisionModel, cls); + } else if (cls.isAssignableFrom(m.getClass())) { + return (Optional) Optional.of(m); + } + return Optional.empty(); + } /** * The shared and static Jackson object mapper used for (de) serialization to diff --git a/java-core/src/main/java/idesyde/core/Explorer.java b/java-core/src/main/java/idesyde/core/Explorer.java index 20afed5b..f8e2249d 100644 --- a/java-core/src/main/java/idesyde/core/Explorer.java +++ b/java-core/src/main/java/idesyde/core/Explorer.java @@ -47,7 +47,7 @@ public interface Explorer { * Give information about the exploration capabilities of this * explorer for a decision model given that other explorers are present. */ - default ExplorationBidding bid(Set explorers, DecisionModel decisionModel) { + default ExplorationBidding bid(DecisionModel decisionModel) { return new ExplorationBidding(false, false, 10.0, Set.of(), Map.of()); } diff --git a/java-core/src/main/java/idesyde/core/IdentificationResult.java b/java-core/src/main/java/idesyde/core/IdentificationResult.java index 7a6989d7..c0124be1 100644 --- a/java-core/src/main/java/idesyde/core/IdentificationResult.java +++ b/java-core/src/main/java/idesyde/core/IdentificationResult.java @@ -6,6 +6,18 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) public record IdentificationResult( - Set identified, - Set messages) { + Set identified, + Set messages) { + + public DecisionModel[] identifiedAsArray() { + return identified().toArray(new DecisionModel[0]); + } + + public String[] messagesAsArray() { + return messages().toArray(new String[0]); + } + + public int part() { + return identified.stream().map(DecisionModel::part).map(Set::size).reduce(0, Integer::sum); + } } diff --git a/java-core/src/main/java/idesyde/core/IdentificationRule.java b/java-core/src/main/java/idesyde/core/IdentificationRule.java index f93f5e82..63024e38 100644 --- a/java-core/src/main/java/idesyde/core/IdentificationRule.java +++ b/java-core/src/main/java/idesyde/core/IdentificationRule.java @@ -1,7 +1,10 @@ package idesyde.core; +import java.util.Arrays; +import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; +import java.util.stream.Collectors; /** * A class that represent an identification rule, including how it partially @@ -12,6 +15,11 @@ public interface IdentificationRule extends BiFunction, Set, IdentificationResult> { + default IdentificationResult fromArrays(DesignModel[] designModels, DecisionModel[] decisionModels) { + return apply(Arrays.stream(designModels).collect(Collectors.toSet()), + Arrays.stream(decisionModels).collect(Collectors.toSet())); + } + default boolean usesDesignModels() { return true; } diff --git a/java-core/src/main/java/idesyde/core/Module.java b/java-core/src/main/java/idesyde/core/Module.java index 7d7b4fe9..67cce3e6 100644 --- a/java-core/src/main/java/idesyde/core/Module.java +++ b/java-core/src/main/java/idesyde/core/Module.java @@ -61,4 +61,30 @@ default Set explorers() { return Set.of(); } + default Set identificationRules() { + return Set.of(); + } + + default Set reverseIdentificationRules() { + return Set.of(); + } + + /** + * This returns the names of the identification rule classes associated with this module. + * It is supposed to be autogenerated with the help of meta-programming tools + * and annotation processing. + */ + default String[] identicationRulesCanonicalClassNames() { + return identificationRules().stream().map(Object::getClass).map(Class::getCanonicalName).collect(Collectors.toSet()).toArray(new String[0]); + } + + /** + * This returns the names of the reverse identification rule classes associated with this module. + * It is supposed to be autogenerated with the help of meta-programming tools + * and annotation processing. + */ + default String[] reverseIdenticationRulesCanonicalClassNames() { + return reverseIdentificationRules().stream().map(Object::getClass).map(Class::getCanonicalName).collect(Collectors.toSet()).toArray(new String[0]); + } + } diff --git a/java-core/src/main/java/idesyde/core/PlainIdentificationResult.java b/java-core/src/main/java/idesyde/core/PlainIdentificationResult.java new file mode 100644 index 00000000..feae67b8 --- /dev/null +++ b/java-core/src/main/java/idesyde/core/PlainIdentificationResult.java @@ -0,0 +1,11 @@ +package idesyde.core; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Set; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +public record PlainIdentificationResult( + DecisionModel[] identified, + String[] messages) { +} diff --git a/java-core/src/main/java/idesyde/core/ReverseIdentificationRule.java b/java-core/src/main/java/idesyde/core/ReverseIdentificationRule.java index fe25827e..7ceecac4 100644 --- a/java-core/src/main/java/idesyde/core/ReverseIdentificationRule.java +++ b/java-core/src/main/java/idesyde/core/ReverseIdentificationRule.java @@ -1,7 +1,10 @@ package idesyde.core; +import java.util.Arrays; import java.util.Set; import java.util.function.BiFunction; +import java.util.stream.Collector; +import java.util.stream.Collectors; /** * A class that represents reverse identification rules. It is not more than a @@ -12,6 +15,10 @@ public interface ReverseIdentificationRule extends BiFunction, Set, Set> { + default Set fromArrays(DecisionModel[] decisionModels, DesignModel[] designModels) { + return this.apply(Arrays.stream(decisionModels).collect(Collectors.toSet()), Arrays.stream(designModels).collect(Collectors.toSet())); + } + /** * A simple wrapper for a function that satisfies the proper reverse * identification rule signature. diff --git a/java-metaheuristics/build.gradle b/java-metaheuristics/build.gradle index 0434776d..c8ceb135 100644 --- a/java-metaheuristics/build.gradle +++ b/java-metaheuristics/build.gradle @@ -13,6 +13,8 @@ dependencies { implementation 'io.jenetics:jenetics:7.1.3' implementation 'io.jenetics:jenetics.ext:7.1.3' implementation 'org.jgrapht:jgrapht-core:1.5.2' + compileOnly project(":java-core-generator") + annotationProcessor project(":java-core-generator") } application { diff --git a/java-metaheuristics/src/main/java/idesyde/metaheuristics/JMetalExplorer.java b/java-metaheuristics/src/main/java/idesyde/metaheuristics/JMetalExplorer.java index ee10516b..bb782cc8 100644 --- a/java-metaheuristics/src/main/java/idesyde/metaheuristics/JMetalExplorer.java +++ b/java-metaheuristics/src/main/java/idesyde/metaheuristics/JMetalExplorer.java @@ -1,5 +1,6 @@ package idesyde.metaheuristics; +import idesyde.core.AutoRegister; import idesyde.core.DecisionModel; import idesyde.core.ExplorationSolution; import idesyde.core.Explorer; @@ -11,12 +12,12 @@ public class JMetalExplorer implements Explorer { @Override - public ExplorationBidding bid(Set explorers, DecisionModel decisionModel) { + public ExplorationBidding bid(DecisionModel decisionModel) { // if (decisionModel instanceof // AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore) { // return new ExplorationBidding(uniqueIdentifer(), true, Map.of()); // } - return Explorer.super.bid(explorers, decisionModel); + return Explorer.super.bid(decisionModel); } @Override diff --git a/java-metaheuristics/src/main/java/idesyde/metaheuristics/JeneticsExplorer.java b/java-metaheuristics/src/main/java/idesyde/metaheuristics/JeneticsExplorer.java index c67bc40a..0b0ce88e 100644 --- a/java-metaheuristics/src/main/java/idesyde/metaheuristics/JeneticsExplorer.java +++ b/java-metaheuristics/src/main/java/idesyde/metaheuristics/JeneticsExplorer.java @@ -3,6 +3,7 @@ import idesyde.common.AperiodicAsynchronousDataflow; import idesyde.common.AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore; import idesyde.common.AperiodicAsynchronousDataflowToPartitionedTiledMulticore; +import idesyde.core.AutoRegister; import idesyde.core.DecisionModel; import idesyde.core.ExplorationSolution; import idesyde.core.Explorer; @@ -16,56 +17,92 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Stream; +@AutoRegister(MetaHeuristicsExplorationModule.class) public class JeneticsExplorer implements Explorer, CanExploreAADPMMMWithJenetics, CanExploreAADPTMWithJenetics { - private Map> _memoizedJobs = new HashMap<>(); - private Map>> _memoizedFollows = new HashMap<>(); - @Override - public ExplorationBidding bid(Set explorers, DecisionModel decisionModel) { - if (decisionModel instanceof AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore) { - var objs = new HashSet(); - objs.add("nUsedPEs"); - for (var app : aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore - .aperiodicAsynchronousDataflows()) { - for (var actor : app.processes()) { - if (!app.processMinimumThroughput().containsKey(actor)) { - objs.add("invThroughput(%s)".formatted(actor)); - } - } - } - return new ExplorationBidding(true, false, 1.1, objs, Map.of("time-to-first", 10.0)); - } else if (decisionModel instanceof AperiodicAsynchronousDataflowToPartitionedTiledMulticore aperiodicAsynchronousDataflowToPartitionedTiledMulticore) { - var objs = new HashSet(); - objs.add("nUsedPEs"); - for (var app : aperiodicAsynchronousDataflowToPartitionedTiledMulticore - .aperiodicAsynchronousDataflows()) { - for (var actor : app.processes()) { - if (!app.processMinimumThroughput().containsKey(actor)) { - objs.add("invThroughput(%s)".formatted(actor)); + public ExplorationBidding bid(DecisionModel decisionModel) { + return DecisionModel + .cast(decisionModel, AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore.class) + .map(aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore -> { + var objs = new HashSet(); + objs.add("nUsedPEs"); + for (var app : aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore + .aperiodicAsynchronousDataflows()) { + for (var actor : app.processes()) { + if (!app.processMinimumThroughput().containsKey(actor)) { + objs.add("invThroughput(%s)".formatted(actor)); + } + } } - } - } - return new ExplorationBidding(true, false, 1.1, objs, Map.of("time-to-first", 10.0)); - } - return Explorer.super.bid(explorers, decisionModel); + return new ExplorationBidding(true, false, 1.1, objs, Map.of("time-to-first", 10.0)); + }) + .or(() -> DecisionModel + .cast(decisionModel, AperiodicAsynchronousDataflowToPartitionedTiledMulticore.class) + .map(m -> { + var objs = new HashSet(); + objs.add("nUsedPEs"); + for (var app : m + .aperiodicAsynchronousDataflows()) { + for (var actor : app.processes()) { + if (!app.processMinimumThroughput().containsKey(actor)) { + objs.add("invThroughput(%s)".formatted(actor)); + } + } + } + return new ExplorationBidding(true, false, 1.1, objs, Map.of("time-to-first", 10.0)); + })) + .orElse(Explorer.super.bid(decisionModel)); + // if (decisionModel instanceof + // AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore + // aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore) { + // var objs = new HashSet(); + // objs.add("nUsedPEs"); + // for (var app : + // aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore + // .aperiodicAsynchronousDataflows()) { + // for (var actor : app.processes()) { + // if (!app.processMinimumThroughput().containsKey(actor)) { + // objs.add("invThroughput(%s)".formatted(actor)); + // } + // } + // } + // return new ExplorationBidding(true, false, 1.1, objs, Map.of("time-to-first", + // 10.0)); + // } else if (decisionModel instanceof + // AperiodicAsynchronousDataflowToPartitionedTiledMulticore + // aperiodicAsynchronousDataflowToPartitionedTiledMulticore) { + // var objs = new HashSet(); + // objs.add("nUsedPEs"); + // for (var app : aperiodicAsynchronousDataflowToPartitionedTiledMulticore + // .aperiodicAsynchronousDataflows()) { + // for (var actor : app.processes()) { + // if (!app.processMinimumThroughput().containsKey(actor)) { + // objs.add("invThroughput(%s)".formatted(actor)); + // } + // } + // } + // return new ExplorationBidding(true, false, 1.1, objs, Map.of("time-to-first", + // 10.0)); + // } + // return Explorer.super.bid(explorers, decisionModel); } @Override public Stream explore(DecisionModel decisionModel, Set previousSolutions, Configuration configuration) { - Stream explorationStream = Stream.empty(); var foundSolutionObjectives = new CopyOnWriteArraySet>(); - if (decisionModel instanceof AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore) { - explorationStream = exploreAADPMMM(aperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore, previousSolutions, - configuration); - } else if (decisionModel instanceof AperiodicAsynchronousDataflowToPartitionedTiledMulticore aperiodicAsynchronousDataflowToPartitionedTiledMulticore) { - explorationStream = exploreAADPTM(aperiodicAsynchronousDataflowToPartitionedTiledMulticore, previousSolutions, - configuration); - } else { - explorationStream = Explorer.super.explore(decisionModel, previousSolutions, configuration); - } - return explorationStream.filter(sol -> !previousSolutions.contains(sol) && !foundSolutionObjectives.contains(sol.objectives())).peek(s -> foundSolutionObjectives.add(s.objectives())); + var explorationStream = DecisionModel + .cast(decisionModel, AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore.class) + .map(m -> exploreAADPMMM(m, previousSolutions, configuration)) + .or(() -> DecisionModel + .cast(decisionModel, AperiodicAsynchronousDataflowToPartitionedTiledMulticore.class) + .map(m -> exploreAADPTM(m, previousSolutions, configuration))) + .orElse(Stream.empty()); + return explorationStream + .filter(sol -> !previousSolutions.contains(sol) && + !foundSolutionObjectives.contains(sol.objectives())) + .peek(s -> foundSolutionObjectives.add(s.objectives())); } // @Override diff --git a/java-metaheuristics/src/main/java/idesyde/metaheuristics/MetaHeuristicsExplorationModule.java b/java-metaheuristics/src/main/java/idesyde/metaheuristics/MetaHeuristicsExplorationModule.java index 2f926c50..9f302c48 100644 --- a/java-metaheuristics/src/main/java/idesyde/metaheuristics/MetaHeuristicsExplorationModule.java +++ b/java-metaheuristics/src/main/java/idesyde/metaheuristics/MetaHeuristicsExplorationModule.java @@ -10,35 +10,28 @@ import java.util.Optional; import java.util.Set; -public class MetaHeuristicsExplorationModule implements StandaloneModule { +public interface MetaHeuristicsExplorationModule extends StandaloneModule { @Override - public Optional fromOpaqueDecision(OpaqueDecisionModel message) { + public default Optional fromOpaqueDecision(OpaqueDecisionModel message) { switch (message.category()) { case "AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore": - return message.bodyCBOR().flatMap(x -> readFromCBORBytes(x, AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore.class)) - .or(() -> message.bodyJson().flatMap(x -> readFromJsonString(x, AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore.class))) + return message.bodyCBOR() + .flatMap(x -> readFromCBORBytes(x, + AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore.class)) + .or(() -> message.bodyJson() + .flatMap(x -> readFromJsonString(x, + AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore.class))) .map(x -> (DecisionModel) x); case "AperiodicAsynchronousDataflowToPartitionedTiledMulticore": - return message.bodyCBOR().flatMap(x -> readFromCBORBytes(x, AperiodicAsynchronousDataflowToPartitionedTiledMulticore.class)) - .or(() -> message.bodyJson().flatMap(x -> readFromJsonString(x, AperiodicAsynchronousDataflowToPartitionedTiledMulticore.class))) + return message.bodyCBOR().flatMap( + x -> readFromCBORBytes(x, AperiodicAsynchronousDataflowToPartitionedTiledMulticore.class)) + .or(() -> message.bodyJson() + .flatMap(x -> readFromJsonString(x, + AperiodicAsynchronousDataflowToPartitionedTiledMulticore.class))) .map(x -> (DecisionModel) x); default: return Optional.empty(); } } - @Override - public String uniqueIdentifier() { - return "MetaHeuristicsExplorationModule"; - } - - @Override - public Set explorers() { - return Set.of(new JeneticsExplorer()); - } - - public static void main(String[] args) { - var server = new MetaHeuristicsExplorationModule().standaloneModule(args); - server.ifPresent(s -> s.start(0)); - } } diff --git a/java-metaheuristics/src/main/java/module-info.java b/java-metaheuristics/src/main/java/module-info.java index 7b035030..a94728ab 100644 --- a/java-metaheuristics/src/main/java/module-info.java +++ b/java-metaheuristics/src/main/java/module-info.java @@ -2,6 +2,7 @@ requires transitive idesyde.core; requires transitive idesyde.common; requires transitive idesyde.blueprints; + requires static idesyde.core.generator; // requires jmetal.core; // requires jmetal.algorithm; diff --git a/project/build.properties b/project/build.properties index 0192d77a..c0df48dd 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.9.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index b94cdd2b..b212e591 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.10") -addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.16") +// addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.16") addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") addSbtPlugin("com.github.sbt" % "sbt-site-paradox" % "1.5.0-RC2") diff --git a/project/project/project/metals.sbt b/project/project/project/metals.sbt index 4c9df445..119c9296 100644 --- a/project/project/project/metals.sbt +++ b/project/project/project/metals.sbt @@ -2,5 +2,5 @@ // This file enables sbt-bloop to create bloop config files. -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.13") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.15") diff --git a/rust-blueprints/src/lib.rs b/rust-blueprints/src/lib.rs index 5b31d8cf..14568057 100644 --- a/rust-blueprints/src/lib.rs +++ b/rust-blueprints/src/lib.rs @@ -9,9 +9,7 @@ use std::{ use clap::Parser; use derive_builder::Builder; use idesyde_core::{ - DecisionModel, DesignModel, ExplorationSolution, Explorer, IdentificationIterator, - IdentificationResult, MarkedIdentificationRule, Module, OpaqueDecisionModel, - ReverseIdentificationRule, + DecisionModel, DesignModel, ExplorationSolution, Explorer, IdentificationResult, IdentificationRuleLike, MarkedIdentificationRule, Module, OpaqueDecisionModel, ReverseIdentificationRule }; use log::debug; use serde::{Deserialize, Serialize}; @@ -116,13 +114,13 @@ impl From<&ExplorationSolutionMessage> for OpaqueDecisionModel { } } -#[derive(Clone, Builder, PartialEq, Eq)] +#[derive(Clone, Builder)] pub struct StandaloneModule { unique_identifier: String, #[builder(default = "Vec::new()")] explorers: Vec>, #[builder(default = "vec![]")] - identification_rules: Vec, + identification_rules: Vec>, #[builder(default = "vec![]")] reverse_identification_rules: Vec, #[builder(default = "|_| { None }")] @@ -151,228 +149,6 @@ impl StandaloneModule { } } -/// A simple iterator for performing identification in-process. -#[derive(Builder, PartialEq, Eq, Clone)] -struct DefaultIdentificationIterator { - #[builder(default = "Vec::new()")] - design_models: Vec>, - #[builder(default = "Vec::new()")] - decision_models: Vec>, - imodule: Arc, -} - -impl Iterator for DefaultIdentificationIterator { - type Item = IdentificationResult; - - fn next(&mut self) -> Option { - // let mut identified = vec![]; - // Assume that all the models which could have been made non-opaque, did. - // let (tx_model, rx_model) = std::sync::mpsc::channel::>(); - // let (tx_msg, rx_msg) = std::sync::mpsc::channel::(); - let par_identified: Vec = self - .imodule - .identification_rules - .par_iter() - .flat_map_iter(|irule| match irule { - MarkedIdentificationRule::DesignModelOnlyIdentificationRule(f) => { - if !self.design_models.is_empty() { - Some(f) - } else { - None - } - } - MarkedIdentificationRule::DecisionModelOnlyIdentificationRule(f) => { - if !self.decision_models.is_empty() { - Some(f) - } else { - None - } - } - MarkedIdentificationRule::GenericIdentificationRule(f) => Some(f), - MarkedIdentificationRule::SpecificDecisionModelIdentificationRule(ms, f) => { - if ms - .iter() - .all(|x| self.decision_models.iter().any(|y| x == &y.category())) - { - Some(f) - } else { - None - } - } - }) - .map(|f| f(&self.design_models, &self.decision_models)) - .map(|(models, msgs)| { - ( - models - .into_iter() - .map(|model| { - model - .downcast_ref::() - .and_then(|opaque| self.imodule.opaque_to_model(opaque)) - .unwrap_or(model) - }) - .collect(), - msgs, - ) - }) - .collect(); - let mut messages = vec![]; - for (ms, msgs) in par_identified { - for m in ms { - if !self.decision_models.contains(&m) { - self.decision_models.push(m); - } - } - for msg in msgs { - if !messages.contains(&msg) { - messages.push(msg); - } - } - } - Some((self.decision_models.clone(), messages)) - } -} - -impl IdentificationIterator for DefaultIdentificationIterator { - fn next_with_models( - &mut self, - decision_models: &Vec>, - design_models: &Vec>, - ) -> Option { - // first, add everything - for m in design_models { - if !self.design_models.contains(m) { - self.design_models.push(m.to_owned()); - } - } - for m in decision_models { - let refined = m - .downcast_ref::() - .and_then(|opaque| self.imodule.opaque_to_model(opaque)) - .unwrap_or(m.to_owned()); - if !self.decision_models.contains(&refined) { - self.decision_models.push(refined.to_owned()); - } - } - return self.next(); - } - - // fn collect_messages(&mut self) -> Vec<(String, String)> { - // self.messages - // .iter() - // .map(|x| ("DEBUG".to_string(), x.to_owned())) - // .collect() - // } -} - -impl Module for StandaloneModule { - fn unique_identifier(&self) -> String { - self.unique_identifier.to_owned() - } - - fn identification_step( - &self, - decision_models: &Vec>, - design_models: &Vec>, - ) -> IdentificationResult { - // Box::new(DefaultIdentificationIteratorBuilder::default() - // .decision_models(initial_decision_models.to_owned()) - // .design_models(initial_design_models.to_owned()) - // .imodule(Arc::new(self.to_owned())) - // .build() - // .expect("Failed to create an identification iterator by an identification module. Should never happen.")) - // Assume that all the models which could have been made non-opaque, did. - // let (tx_model, rx_model) = std::sync::mpsc::channel::>(); - // let (tx_msg, rx_msg) = std::sync::mpsc::channel::(); - let mut identified_models = vec![]; - for m in decision_models { - if let Some(refined) = m.downcast_ref::().and_then(self.opaque_to_model) { - // debug!("Refining a {}", refined.category()); - identified_models.push(refined); - } else { - identified_models.push(m.clone()); - } - } - let par_identified: Vec = self - .identification_rules - .par_iter() - .flat_map_iter(|irule| match irule { - MarkedIdentificationRule::DesignModelOnlyIdentificationRule(f) => { - if !design_models.is_empty() { - Some(f) - } else { - None - } - } - MarkedIdentificationRule::DecisionModelOnlyIdentificationRule(f) => { - if !decision_models.is_empty() { - Some(f) - } else { - None - } - } - MarkedIdentificationRule::GenericIdentificationRule(f) => Some(f), - MarkedIdentificationRule::SpecificDecisionModelIdentificationRule(ms, f) => { - if ms - .iter() - .all(|x| decision_models.iter().any(|y| x == &y.category())) - { - Some(f) - } else { - None - } - } - }) - .map(|f| f(&design_models, &identified_models)) - .map(|(models, msgs)| { - ( - models - .into_iter() - .map(|model| { - model - .downcast_ref::() - .and_then(self.opaque_to_model) - .unwrap_or(model) - }) - .collect(), - msgs, - ) - }) - .collect(); - let mut messages = vec![]; - for (ms, msgs) in par_identified { - for m in ms { - if !identified_models.contains(&m) { - identified_models.push(m); - } - } - for msg in msgs { - if !messages.contains(&msg) { - messages.push(msg); - } - } - } - (identified_models, messages) - } - - fn reverse_identification( - &self, - solved_decision_models: &Vec>, - design_models: &Vec>, - ) -> Vec> { - let decs = solved_decision_models.to_owned(); - let dess = design_models.to_owned(); - self.reverse_identification_rules - .par_iter() - .flat_map(move |f| f(&decs, &dess)) - .collect() - } - - fn explorers(&self) -> Vec> { - self.explorers.to_owned() - } -} - #[derive(Parser, Debug)] #[command(author = "Rodolfo Jordao")] pub struct ModuleArgs { diff --git a/rust-bridge-java/Cargo.toml b/rust-bridge-java/Cargo.toml new file mode 100644 index 00000000..3423b2f3 --- /dev/null +++ b/rust-bridge-java/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "idesyde-bridge-java" +version.workspace = true +authors.workspace = true +edition.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +idesyde-core = { path = "../rust-core" } +idesyde-blueprints = { path = "../rust-blueprints" } +idesyde-common = { path = "../rust-common" } +derive_builder.workspace = true +jni.workspace = true +zip.workspace = true diff --git a/rust-bridge-java/src/lib.rs b/rust-bridge-java/src/lib.rs new file mode 100644 index 00000000..4e341f9d --- /dev/null +++ b/rust-bridge-java/src/lib.rs @@ -0,0 +1,1149 @@ +use std::{ + collections::{HashMap, HashSet}, + hash::Hash, + io::Read, + sync::{Arc, Mutex}, +}; + +use idesyde_core::{ + DecisionModel, DesignModel, ExplorationBid, ExplorationConfiguration, ExplorationSolution, + Explorer, IdentificationResult, IdentificationRuleLike, LoggedResult, Module, + OpaqueDecisionModel, OpaqueDesignModel, ReverseIdentificationResult, + ReverseIdentificationRuleLike, +}; +use jni::{ + objects::{GlobalRef, JObject, JObjectArray, JPrimitiveArray, JString, JValue}, + InitArgsBuilder, JNIEnv, JNIVersion, JavaVM, +}; +use zip::ZipArchive; + +trait FromJava<'a, T>: Sized +where + T: Into>, +{ + fn from_java(env: &mut JNIEnv<'a>, obj: T) -> Result; +} + +trait IntoJava<'a, T> +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result; +} + +impl<'a, T> IntoJava<'a, T> for f64 +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result { + env.with_local_frame_returning_local(32, |inner| { + let cls = inner.find_class("java/lang/Double")?; + inner + .call_static_method( + cls, + "valueOf", + "(D)Ljava/lang/Double;", + &[JValue::Double(*self)], + ) + .and_then(|x| x.l()) + }) + .map(|x| T::from(x)) + } +} + +impl<'a, T> IntoJava<'a, T> for u64 +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result { + env.with_local_frame_returning_local(32, |inner| { + let cls = inner.find_class("java/lang/Long")?; + inner + .call_static_method( + cls, + "valueOf", + "(I)Ljava/lang/Long;", + &[JValue::Long(*self as i64)], + ) + .and_then(|x| x.l()) + }) + .map(|x| T::from(x)) + } +} + +impl<'a, T> IntoJava<'a, T> for bool +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result { + env.with_local_frame_returning_local(32, |inner| { + let cls = inner.find_class("java/lang/Boolean")?; + inner + .call_static_method( + cls, + "valueOf", + "(I)Ljava/lang/Boolean;", + &[JValue::Bool(*self as u8)], + ) + .and_then(|x| x.l()) + }) + .map(|x| T::from(x)) + } +} + +impl<'a, T> IntoJava<'a, T> for String +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result { + env.with_local_frame_returning_local(self.len() as i32, |inner| { + inner.new_string(self).map(|s| JObject::from(s)) + }) + .map(|x| T::from(x)) + } +} + +impl<'a, T> IntoJava<'a, T> for &[u8] +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result { + env.with_local_frame_returning_local(2 + 2 * self.len() as i32, |inner| { + inner.byte_array_from_slice(self).map(|x| JObject::from(x)) + }) + .map(|o| T::from(o)) + } +} + +impl<'a, T> IntoJava<'a, T> for Vec +where + T: From>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result { + self.as_slice().into_java(env) + } +} + +impl<'a, T> IntoJava<'a, JObject<'a>> for Option +where + T: IntoJava<'a, JObject<'a>>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let optional_class = env.find_class("java/util/Optional")?; + match self { + Some(x) => x.into_java(env).and_then(|javax| { + env.with_local_frame_returning_local(32, |inner| { + inner + .call_static_method( + optional_class, + "of", + "(Ljava/lang/Object;)Ljava/util/Optional;", + &[JValue::Object(&javax.into())], + ) + .and_then(|y| y.l()) + }) + }), + None => env.with_local_frame_returning_local(32, |inner| { + inner + .call_static_method(optional_class, "empty", "()Ljava/util/Optional;", &[]) + .and_then(|x| x.l()) + }), + } + } +} + +impl<'a, T> IntoJava<'a, JObject<'a>> for HashSet +where + T: IntoJava<'a, JObject<'a>>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let set_class = env.find_class("java/util/HashSet")?; + let set = env.with_local_frame_returning_local(16 + 2 * self.len() as i32, |inner| { + inner.new_object(set_class, "()V", &[]) + })?; + for elem in self { + let elem = elem.into_java(env)?; + env.call_method( + &set, + "add", + "(Ljava/lang/Object;)Z", + &[JValue::Object(elem.as_ref())], + )?; + } + Ok(set) + } +} + +impl<'a, K, V> IntoJava<'a, JObject<'a>> for HashMap +where + K: IntoJava<'a, JObject<'a>>, + V: IntoJava<'a, JObject<'a>>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let map_cls = env.find_class("java/util/HashMap")?; + let mapping = env + .with_local_frame_returning_local(16 + 2 * self.len() as i32, |inner| { + inner.new_object(map_cls, "()V", &[]) + })?; + for (key, val) in self { + let java_key = key.into_java(env)?; + let elem = val.into_java(env)?; + env.call_method( + &mapping, + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + &[JValue::Object(&java_key), JValue::Object(&elem)], + )?; + } + Ok(mapping) + } +} + +impl<'a, T> IntoJava<'a, JObjectArray<'a>> for &[T] +where + T: IntoJava<'a, JObject<'a>>, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let cls = env.find_class("java/lang/Object")?; + if let Some(fst) = self.first() { + let fst_java = fst.into_java(env)?; + let array = env + .with_local_frame_returning_local(16 + 2 * self.len() as i32, |inner| { + inner + .new_object_array(self.len() as i32, &cls, fst_java) + .map(|o| JObject::from(o)) + }) + .map(|x| JObjectArray::from(x))?; + for i in 1..self.len() { + let x = &self[i]; + let elem = x.into_java(env)?; + env.set_object_array_element(&array, i as i32, elem)?; + // if let Some(elem) = self.get(i).and_then(|x| x.into_java(inner).ok()) { + // inner.set_object_array_element(&array, i as i32, elem)?; + // } + } + Ok(array) + } else { + env.new_object_array(0, &cls, jni::objects::JObject::null()) + .map(|x| JObjectArray::from(x)) + } + } +} + +impl<'a> IntoJava<'a, JObject<'a>> for OpaqueDesignModel { + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let opaque_class = env.find_class("idesyde/core/OpaqueDesignModel")?; + env.with_local_frame_returning_local(128 as i32, |inner| { + let category: JString = self.category().into_java(inner)?; + let format: JString = self.format().into_java(inner)?; + let body = self + .body_as_string() + .and_then(|x| x.into_java(inner).ok()) + .unwrap_or( + inner + .new_string("") + .expect("Should not fail to create an empty string."), + ); + let elems = self.elements().into_java(inner)?; + inner.new_object( + opaque_class, + "(Ljava/lang/String;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)V", + &[ + JValue::Object(category.as_ref()), + JValue::Object(elems.as_ref()), + JValue::Object(format.as_ref()), + JValue::Object(body.as_ref()), + ], + ) + }) + } +} + +impl<'a> IntoJava<'a, JObject<'a>> for dyn DesignModel { + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + OpaqueDesignModel::from(self).into_java(env) + } +} + +impl<'a> IntoJava<'a, JObject<'a>> for OpaqueDecisionModel { + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let opaque_class = env.find_class("idesyde/core/OpaqueDecisionModel")?; + env.with_local_frame_returning_local(self.part().len() as i32 + 128, |inner| { + let category: JString = self.category().into_java(inner)?; + let part = self.part().into_java(inner)?; + let body_json = self.body_as_json().into_java(inner)?; + let body_msgpack = self.body_as_msgpack().into_java(inner)?; + let body_cbor = self.body_as_cbor().into_java(inner)?; + inner.new_object(opaque_class, "(Ljava/lang/String;Ljava/util/Set;Ljava/util/Optional;Ljava/util/Optional;Ljava/util/Optional;)V", &[ + JValue::Object(category.as_ref()), + JValue::Object(part.as_ref()), + JValue::Object(body_json.as_ref()), + JValue::Object(body_msgpack.as_ref()), + JValue::Object(body_cbor.as_ref()) + ]) + }) + } +} + +impl<'a> IntoJava<'a, JObject<'a>> for dyn DecisionModel { + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + OpaqueDecisionModel::from(self).into_java(env) + } +} + +impl<'a, T> IntoJava<'a, JObject<'a>> for Arc +where + T: IntoJava<'a, JObject<'a>> + ?Sized, +{ + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + self.as_ref().into_java(env) + } +} + +impl<'a> IntoJava<'a, JObject<'a>> for ExplorationSolution { + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let sols = self.objectives.into_java(env)?; + let decision = self.solved.into_java(env)?; + env.with_local_frame_returning_local(128, |inner| { + let cls = inner.find_class("idesyde/core/ExplorationSolution")?; + inner.new_object( + cls, + "(Ljava/util/Map;Lidesyde/core/DecisionModel;)V", + &[JValue::Object(&sols), JValue::Object(&decision)], + ) + }) + } +} + +impl<'a> IntoJava<'a, JObject<'a>> for ExplorationConfiguration { + fn into_java(&self, env: &mut JNIEnv<'a>) -> Result, jni::errors::Error> { + let cls = env.find_class("idesyde/core/Explorer$Configuration")?; + // let max_sols: JObject = self.max_sols.into_java(env)?; + // let total_timeout: JObject = self.total_timeout.into_java(env)?; + // let improvement_timeout: JObject = self.improvement_timeout.into_java(env)?; + // let time_resolution: JObject = self.time_resolution.into_java(env)?; + // let memory_resolution: JObject = self.memory_resolution.into_java(env)?; + // let improvement_iterations: JObject = self.improvement_iterations.into_java(env)?; + // let strict: JObject = self.strict.into_java(env)?; + let target_objectives = self.target_objectives.into_java(env)?; + env.with_local_frame_returning_local(128, |inner| { + let conf = inner.new_object(cls, "()V", &[])?; + inner.set_field( + &conf, + "totalExplorationTimeOutInSecs", + "J", + JValue::Long(self.total_timeout as i64), + )?; + inner.set_field( + &conf, + "improvementTimeOutInSecs", + "J", + JValue::Long(self.improvement_timeout as i64), + )?; + inner.set_field( + &conf, + "maximumSolutions", + "J", + JValue::Long(self.max_sols as i64), + )?; + inner.set_field( + &conf, + "improvementIterations", + "J", + JValue::Long(self.improvement_iterations as i64), + )?; + inner.set_field( + &conf, + "timeDiscretizationFactor", + "J", + JValue::Long(self.time_resolution as i64), + )?; + inner.set_field( + &conf, + "memoryDiscretizationFactor", + "J", + JValue::Long(self.memory_resolution as i64), + )?; + inner.set_field(&conf, "strict", "Z", JValue::Bool(self.strict as u8))?; + inner.set_field( + &conf, + "targetObjectives", + "Ljava/util/Set;", + JValue::Object(&target_objectives), + )?; + Ok(conf) + }) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for f64 { + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { + env.with_local_frame(32, |inner| { + inner + .call_method(&obj, "doubleValue", "()D", &[]) + .and_then(|x| x.d()) + }) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for String { + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { + env.with_local_frame_returning_local(256, |inner| { + inner + .call_method(&obj, "toString", "()Ljava/lang/String;", &[]) + .and_then(|x| x.l()) + }) + .map(|s| JString::from(s)) + .and_then(|s| { + env.get_string(&s) + .map(|ins| ins.to_str().map(|x| x.to_owned()).unwrap_or("".to_string())) + }) + } +} + +impl<'a> FromJava<'a, JString<'a>> for String { + fn from_java(env: &mut JNIEnv<'a>, obj: JString<'a>) -> Result { + env.get_string(&obj) + .map(|x| x.to_str().map(|x| x.to_owned()).unwrap_or("".to_string())) + } +} + +impl<'a, T> FromJava<'a, JObject<'a>> for Option +where + T: Sized + FromJava<'a, JObject<'a>>, +{ + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result, jni::errors::Error> { + env.ensure_local_capacity(16 as i32)?; + let is_present = env.call_method(&obj, "isPresent", "()Z", &[])?; + if let Ok(true) = is_present.z() { + let opt = env + .call_method(&obj, "get", "()Ljava/lang/Object;", &[])? + .l()?; + let inside = T::from_java(env, opt)?; + Ok(Some(inside)) + } else { + Ok(None) + } + } +} + +impl<'a, T> FromJava<'a, JObject<'a>> for Arc +where + T: FromJava<'a, JObject<'a>> + ?Sized, +{ + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result, jni::errors::Error> { + T::from_java(env, obj).map(|x| Arc::new(x)) + } +} + +impl<'a, T> FromJava<'a, JObject<'a>> for HashSet +where + T: Eq + PartialEq + Hash + FromJava<'a, JObject<'a>>, +{ + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result, jni::errors::Error> { + let mut set: HashSet = HashSet::new(); + let set_size = env.call_method(&obj, "size", "()I", &[])?.i()?; + env.ensure_local_capacity(128 as i32 + 2 * set_size)?; + let iter = env + .call_method(&obj, "iterator", "()Ljava/util/Iterator;", &[]) + .and_then(|x| x.l())?; + while env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z())? + == true + { + let elem = env + .call_method(&iter, "next", "()Ljava/lang/Object;", &[])? + .l()?; + let elem = T::from_java(env, elem)?; + set.insert(elem); + } + Ok(set) + } +} + +impl<'a, K, V> FromJava<'a, JObject<'a>> for HashMap +where + K: Eq + PartialEq + Hash + FromJava<'a, JObject<'a>>, + V: FromJava<'a, JObject<'a>>, +{ + fn from_java( + env: &mut JNIEnv<'a>, + obj: JObject<'a>, + ) -> Result, jni::errors::Error> { + let mut mapping: HashMap = HashMap::new(); + let iter = env.with_local_frame_returning_local(32, |inner| { + let entry_set = inner + .call_method(&obj, "entrySet", "()Ljava/util/Set;", &[]) + .and_then(|x| x.l())?; + inner + .call_method(&entry_set, "iterator", "()Ljava/util/Iterator;", &[]) + .and_then(|x| x.l()) + })?; + while env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z())? + == true + { + let elem = env + .call_method(&iter, "next", "()Ljava/lang/Object;", &[])? + .l()?; + let key_java = env + .call_method(&elem, "getKey", "()Ljava/lang/Object;", &[]) + .and_then(|x| x.l())?; + let key = K::from_java(env, key_java)?; + let val_java = env + .call_method(&elem, "getValue", "()Ljava/lang/Object;", &[]) + .and_then(|x| x.l())?; + let val = V::from_java(env, val_java)?; + mapping.insert(key, val); + } + Ok(mapping) + } +} + +impl<'a, T> FromJava<'a, JObject<'a>> for Vec +where + T: PartialEq + FromJava<'a, JObject<'a>>, +{ + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result, jni::errors::Error> { + let mut vector: Vec = vec![]; + let vec_size = env.call_method(&obj, "size", "()I", &[])?.i()?; + env.ensure_local_capacity(128 as i32 + 2 * vec_size)?; + let iter = env + .call_method(&obj, "iterator", "()Ljava/util/Iterator;", &[]) + .and_then(|x| x.l())?; + while env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z())? + == true + { + let elem = env + .call_method(&iter, "next", "()Ljava/lang/Object;", &[])? + .l()?; + let elem = T::from_java(env, elem)?; + vector.push(elem); + } + Ok(vector) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for Vec { + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result, jni::errors::Error> { + let arr = JPrimitiveArray::from(obj); + env.convert_byte_array(arr) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for OpaqueDecisionModel { + fn from_java( + env: &mut JNIEnv<'a>, + obj: JObject<'a>, + ) -> Result { + let mut builder = OpaqueDecisionModel::builder(); + env.with_local_frame(512, |inner| { + let category_obj = inner + .call_method(&obj, "category", "()Ljava/lang/String;", &[])? + .l()?; + builder.category(String::from_java(inner, JString::from(category_obj))?); + let json_obj = inner + .call_method(&obj, "asJsonString", "()Ljava/util/Optional;", &[])? + .l()?; + builder.body_json(Option::from_java(inner, json_obj)?); + let cbor_obj = inner + .call_method(&obj, "asCBORBinary", "()Ljava/util/Optional;", &[])? + .l()?; + builder.body_cbor(Option::from_java(inner, cbor_obj)?); + let part = inner + .call_method(&obj, "part", "()Ljava/util/Set;", &[])? + .l()?; + builder.part(HashSet::from_java(inner, part)?); + Ok(builder + .body_msgpack(None) + .build() + .expect("Failed to build opaque decision model. Should not happen")) + }) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for OpaqueDesignModel { + fn from_java( + env: &mut JNIEnv<'a>, + obj: JObject<'a>, + ) -> Result { + let mut builder = OpaqueDesignModel::builder(); + env.with_local_frame(512, |inner| { + let category_obj = inner + .call_method(&obj, "category", "()Ljava/lang/String;", &[])? + .l()?; + builder.category(String::from_java(inner, JString::from(category_obj))?); + let format_obj = inner + .call_method(&obj, "format", "()Ljava/lang/String;", &[])? + .l()?; + builder.format(String::from_java(inner, JString::from(format_obj))?); + let body_obj = inner + .call_method(&obj, "asString", "()Ljava/util/Optional;", &[])? + .l()?; + builder.body(Option::from_java(inner, body_obj)?); + let elems = inner + .call_method(&obj, "elements", "()Ljava/util/Set;", &[])? + .l()?; + builder.elements(HashSet::from_java(inner, elems)?); + Ok(builder + .build() + .expect("Failed to build opaque decision model. Should not happen")) + }) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for IdentificationResult { + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { + // TODO: fix this conservative memory allocation here + let decisions: HashSet = env + .call_method(&obj, "identified", "()Ljava/util/Set;", &[]) + .and_then(|x| x.l()) + .and_then(|x| HashSet::from_java(env, x))?; + let dyn_decisions = decisions + .into_iter() + .map(|x| Arc::new(x) as Arc) + .collect(); + let messages: HashSet = env + .call_method(&obj, "messages", "()Ljava/util/Set;", &[]) + .and_then(|x| x.l()) + .and_then(|x| HashSet::from_java(env, x))?; + Ok((dyn_decisions, messages.into_iter().collect())) + } +} + +// impl<'a> FromJava<'a, JObject<'a>> for dyn DecisionModel { +// fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { +// OpaqueDecisionModel::from_java(env, obj).map(|x| &x as &dyn DecisionModel) +// } +// } + +// impl<'a> FromJava<'a, JObject<'a>> for dyn DesignModel { +// fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { +// OpaqueDesignModel::from_java(env, obj).map(|x| &x as &dyn DesignModel) +// } +// } + +impl<'a> FromJava<'a, JObject<'a>> for ExplorationBid { + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { + let mut builder = ExplorationBid::builder(); + env.with_local_frame(512, |inner| { + let objs_set: HashSet = inner + .call_method(&obj, "targetObjectives", "()Ljava/util/Set;", &[]) + .and_then(|x| x.l()) + .and_then(|x| HashSet::from_java(inner, x))?; + builder.target_objectives(objs_set); + let can_explore = inner + .call_method(&obj, "canExplore", "()Ljava/lang/Boolean;", &[]) + .and_then(|x| x.l()) + .and_then(|x| inner.call_method(&x, "booleanValue", "()Z", &[])) + .and_then(|x| x.z()) + .unwrap_or(false); + builder.can_explore(can_explore); + let competitiveness = inner + .call_method(&obj, "competitiveness", "()Ljava/lang/Double;", &[]) + .and_then(|x| x.l()) + .and_then(|x| inner.call_method(&x, "doubleValue", "()D", &[])) + .and_then(|x| x.d()) + .map(|f| f as f32) + .unwrap_or(100.0f32); + builder.competitiveness(competitiveness); + let is_exact = inner + .call_method(&obj, "isExact", "()Ljava/lang/Boolean;", &[]) + .and_then(|x| x.l()) + .and_then(|x| inner.call_method(&x, "booleanValue", "()Z", &[])) + .and_then(|x| x.z()) + .unwrap_or(false); + builder.is_exact(is_exact); + Ok(builder + .build() + .expect("Should never fail to build a bidding.")) + }) + } +} + +impl<'a> FromJava<'a, JObject<'a>> for ExplorationSolution { + fn from_java(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { + let java_model: JObject = env + .call_method(&obj, "solved", "()Lidesyde/core/DecisionModel;", &[])? + .l()?; + let java_opaque = env + .call_static_method( + "idesyde/core/OpaqueDecisionModel", + "from", + "(Lidesyde/core/DecisionModel;)Lidesyde/core/OpaqueDecisionModel;", + &[JValue::Object(&java_model)], + )? + .l()?; + let solved = Arc::new(OpaqueDecisionModel::from_java(env, java_opaque)?); + let objectives: HashMap = env + .call_method(&obj, "objectives", "()Ljava/util/Map;", &[]) + .and_then(|x| x.l()) + .and_then(|x| HashMap::from_java(env, x))?; + Ok(ExplorationSolution { + solved: solved, + objectives: objectives, + }) + } +} + +#[derive(Clone)] +struct JavaModuleIdentificationRule { + pub java_vm: Arc, + pub irule_jobject: GlobalRef, +} + +impl IdentificationRuleLike for JavaModuleIdentificationRule { + fn identify( + &self, + design_models: &[Arc], + decision_models: &[Arc], + ) -> idesyde_core::IdentificationResult { + let mut identified: Vec> = vec![]; + let mut messages: Vec = vec![]; + if let Ok(mut env_root) = self.java_vm.attach_current_thread_permanently() { + let jresult = env_root.with_local_frame(128 + 2 * design_models.iter().map(|x| x.elements().len()).sum::() as i32, |env| { + let jdesigns = design_models.into_java(env)?; + let jdecisions = decision_models.into_java(env)?; + match env.call_method( + &self.irule_jobject, + "fromArrays", + "([Lidesyde/core/DesignModel;[Lidesyde/core/DecisionModel;)Lidesyde/core/IdentificationResult;", + &[ + JValue::Object(jdesigns.as_ref()), + JValue::Object(jdecisions.as_ref()), + ], + ) { + Ok(irecord) => { + return irecord + .l() + .and_then(|result| IdentificationResult::from_java(env, result)) + } + Err(e) => { + messages.push(format!("[]{}", e)); + } + } + Err(jni::errors::Error::JavaException) + }); + match jresult { + Ok((ms, msgs)) => { + identified.extend(ms.into_iter()); + messages.extend(msgs.into_iter()); + } + Err(e) => { + messages.push(format!("[] {}", e)); + } + } + } + (identified, messages) + } + + fn uses_design_models(&self) -> bool { + true + } + + fn uses_decision_models(&self) -> bool { + true + } + + fn uses_specific_decision_models(&self) -> Option> { + None + } +} +struct JavaModuleReverseIdentificationRule { + pub java_vm: Arc, + pub rrule_jobject: Arc, +} + +impl ReverseIdentificationRuleLike for JavaModuleReverseIdentificationRule { + fn reverse_identify( + &self, + decision_models: &[Arc], + design_models: &[Arc], + ) -> idesyde_core::ReverseIdentificationResult { + let mut reversed: Vec = vec![]; + let messages: Vec = vec![]; + if let Ok(mut env_root) = self.java_vm.attach_current_thread_permanently() { + let jresult = + env_root.with_local_frame(128, |env| { + let jdesigns = design_models.into_java(env)?; + let jdecisions = decision_models.into_java(env)?; + let set_obj = env.call_method( + self.rrule_jobject.as_ref(), + "fromArrays", + "([Lidesyde/core/DecisionModel;[Lidesyde/core/DesignModel;)Ljava/util/Set;", + &[ + JValue::Object(jdecisions.as_ref()), + JValue::Object(jdesigns.as_ref()), + ], + )?.l()?; + HashSet::from_java(env, set_obj) + }); + match jresult { + Ok(reversed_set) => { + reversed.extend(reversed_set.into_iter()); + } + Err(e) => println!("[] {}", e), + } + } + ( + reversed + .into_iter() + .map(|x| Arc::new(x) as Arc) + .collect(), + messages, + ) + } +} + +fn instantiate_java_vm_debug( + jar_files: &[std::path::PathBuf], +) -> Result { + let mut builder = InitArgsBuilder::new() + // Pass the JNI API version (default is 8) + .version(JNIVersion::V8); + if cfg!(debug_assertions) { + builder = builder.option("-Xcheck:jni"); + } + if !jar_files.is_empty() { + let path_str = jar_files + .iter() + .map(|x| x.to_str().unwrap_or(".")) + .collect::>() + .join(":"); + builder = builder.option(format!("-Djava.class.path={}", path_str)); + } + JavaVM::new( + builder + .build() + .expect("Init args should not fail to be built"), + ) +} + +#[derive(Clone)] +pub struct JavaModuleExplorer { + pub java_vm: Arc, + pub explorer_jobject: GlobalRef, +} + +impl Explorer for JavaModuleExplorer { + fn unique_identifier(&self) -> String { + self.java_vm + .attach_current_thread_permanently() + .and_then(|mut env| { + env.call_method( + &self.explorer_jobject, + "uniqueIdentifier", + "()Ljava/lang/String;", + &[], + ) + .and_then(|x| x.l()) + .and_then(|x| { + env.get_string(&JString::from(x)).map(|s| { + s.to_str() + .expect("[] Failed converting name to UTF8") + .to_string() + }) + }) + }) + .expect("[] Could not load java module explorer's unique identifier.") + } + + fn bid(&self, m: Arc) -> idesyde_core::ExplorationBid { + if let Ok(mut root_env) = self.java_vm.attach_current_thread_permanently() { + let size_estimate = 3 * m.part().len() as i32; + let java_bid_opt = root_env.with_local_frame_returning_local(size_estimate, |env| { + let jmodel = m + .into_java(env) + .expect("Failed to convert decision model to java opaque"); + env.call_method( + &self.explorer_jobject, + "bid", + "(Lidesyde/core/DecisionModel;)Lidesyde/core/ExplorationBidding;", + &[JValue::Object(jmodel.as_ref())], + ) + .and_then(|x| x.l()) + }); + if let Ok(java_bid) = java_bid_opt { + return ExplorationBid::from_java(&mut root_env, java_bid) + .unwrap_or(ExplorationBid::impossible()); + } + } + idesyde_core::ExplorationBid::impossible() + } + + fn explore( + &self, + m: Arc, + currrent_solutions: &HashSet, + exploration_configuration: idesyde_core::ExplorationConfiguration, + ) -> Arc + Send + Sync>> { + let java_vm = self.java_vm.clone(); + let exploration_iter = java_vm.attach_current_thread_permanently().and_then(|mut top_env| { + let java_m = m.into_java(&mut top_env)?; + let java_sols = currrent_solutions.into_java(&mut top_env)?; + let java_conf = exploration_configuration.into_java(&mut top_env)?; + let iter = top_env.with_local_frame_returning_local(1024, |env| { + let stream = env.call_method(&self.explorer_jobject, "explore", "(Lidesyde/core/DecisionModel;Ljava/util/Set;Lidesyde/core/Explorer$Configuration;)Ljava/util/stream/Stream;", &[ + JValue::Object(&java_m), + JValue::Object(&java_sols), + JValue::Object(&java_conf) + ])?.l()?; + env.call_method(&stream, "iterator", "()Ljava/util/Iterator;", &[])?.l() + })?; + top_env.new_global_ref(iter) + }); + if let Ok(iter) = exploration_iter { + Arc::new(Mutex::new( + std::iter::repeat_with(move || { + if let Ok(mut top_env) = java_vm.attach_current_thread_permanently() { + let has_next = top_env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z()); + if has_next.map(|x| x == true).unwrap_or(false) { + let next_java_opt = top_env.with_local_frame_returning_local(1024, |env| { + env.call_method(&iter, "next", "()Ljava/lang/Object;", &[])?.l() + }); + if let Ok(next_java) = next_java_opt { + return ExplorationSolution::from_java(&mut top_env, next_java) + .ok(); + } + } + } + None + }) + .take_while(|x| x.is_some()) + .flatten(), + )) + } else { + Arc::new(Mutex::new(std::iter::empty())) + } + } +} + +#[derive(Clone)] +pub struct JavaModule { + pub java_vm: Arc, + pub module_jobject: GlobalRef, + pub module_classes_canonical_name: String, +} + +pub fn java_modules_from_jar_paths(paths: &[std::path::PathBuf]) -> LoggedResult> { + let mut modules = vec![]; + let mut warns = vec![]; + match instantiate_java_vm_debug(paths) { + Ok(java_vm) => { + let java_vm_arc = Arc::new(java_vm); + for path in paths { + match std::fs::File::open(path) { + Ok(f) => match ZipArchive::new(f) { + Ok(mut jarfile) => match jarfile.by_name("META-INF/idesyde/automodules") { + Ok(mut automodules) => { + let mut contents = String::new(); + if automodules.read_to_string(&mut contents).is_ok() { + for line in contents.lines() { + let module_jobject = java_vm_arc + .attach_current_thread_permanently() + .and_then(|mut env| { + env.ensure_local_capacity(100 as i32)?; + env.find_class(line.replace('.', "/")) + .and_then(|module_class| { + env.new_object(module_class, "()V", &[]) + }) + .and_then(|module| env.new_global_ref(module)) + }); + if let Ok(global_ref) = module_jobject { + modules.push(JavaModule { + java_vm: java_vm_arc.clone(), + module_classes_canonical_name: line.to_string(), + module_jobject: global_ref, + }); + } + } + }; + } + Err(_) => warns.push(format!( + "Could not open Manifest marker for JAR {}.", + path.display() + )), + }, + Err(_) => { + warns.push(format!("Failed to open as a JAR {}.", path.display())) + } + }, + Err(_) => warns.push(format!("Failed to open file {}.", path.display())), + } + } + } + Err(_) => warns.push("Failed to instantiate Java VM".to_string()), + } + LoggedResult::builder() + .result(modules) + .warn(warns) + .build() + .expect("LoggedResult should never fail to be built") +} + +impl Module for JavaModule { + fn unique_identifier(&self) -> String { + self.java_vm + .attach_current_thread_permanently() + .and_then(|mut env| { + env.call_method( + &self.module_jobject, + "uniqueIdentifier", + "()Ljava/lang/String;", + &[], + ) + .and_then(|x| x.l()) + .and_then(|x| { + env.get_string(&JString::from(x)).map(|s| { + s.to_str() + .expect("[] Failed converting name to UTF8") + .to_string() + }) + }) + }) + .expect("[] Could not load java module explorer's unique identifier.") + } + + fn identification_rules(&self) -> Vec> { + let mut irules: Vec> = vec![]; + if let Ok(mut env) = self.java_vm.attach_current_thread_permanently() { + match env + .call_method( + &self.module_jobject, + "identificationRules", + "()Ljava/util/Set;", + &[], + ) + .and_then(|x| x.l()) + { + Ok(irules_objs) => { + let iter = env + .call_method(irules_objs, "iterator", "()Ljava/util/Iterator;", &[]) + .and_then(|x| x.l()) + .expect("Set to iterator should never fail"); + while env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z()) + .expect("Failed to get boolean from hasNext") + == true + { + let irule_obj = env + .call_method(&iter, "next", "()Ljava/lang/Object;", &[]) + .expect("Failed to call next") + .l() + .expect("Failed to get object from next"); + let irule = JavaModuleIdentificationRule { + java_vm: self.java_vm.clone(), + irule_jobject: env.new_global_ref(irule_obj).expect( + "Failed to make an irule a global variable. Should not happen.", + ), + }; + irules.push(Arc::new(irule)); + } + } + Err(e) => println!("Error: {}", e), + } + } + irules + } + + fn explorers(&self) -> Vec> { + let mut explorers: Vec> = vec![]; + if let Ok(mut env) = self.java_vm.attach_current_thread_permanently() { + match env + .call_method(&self.module_jobject, "explorers", "()Ljava/util/Set;", &[]) + .and_then(|x| x.l()) + { + Ok(explorers_objs) => { + let iter = env + .call_method(explorers_objs, "iterator", "()Ljava/util/Iterator;", &[]) + .and_then(|x| x.l()) + .expect("Set to iterator should never fail"); + while env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z()) + .expect("Failed to get boolean from hasNext") + == true + { + let explorer_obj = env + .call_method(&iter, "next", "()Ljava/lang/Object;", &[]) + .expect("Failed to call next") + .l() + .expect("Failed to get object from next"); + let explorer = JavaModuleExplorer { + java_vm: self.java_vm.clone(), + explorer_jobject: env.new_global_ref(explorer_obj).expect( + "Failed to make an irule a global variable. Should not happen.", + ), + }; + explorers.push(Arc::new(explorer)); + } + } + Err(e) => println!("Error: {}", e), + } + } + explorers + } + + fn reverse_identification_rules(&self) -> Vec> { + let mut rrules: Vec> = vec![]; + if let Ok(mut env) = self.java_vm.attach_current_thread_permanently() { + if let Ok(irules_set_obj) = env + .call_method( + &self.module_jobject, + "reverseIdentificationRules", + "()Ljava/util/Set;", + &[], + ) + .and_then(|x| x.l()) + { + let iter = env + .call_method(&irules_set_obj, "iterator", "()Ljava/util/Iterator;", &[]) + .and_then(|x| x.l()) + .expect("Set to iterator should never fail"); + while env + .call_method(&iter, "hasNext", "()Z", &[]) + .and_then(|x| x.z()) + .expect("Failed to get boolean from hasNext") + == true + { + let rrule_obj = env + .call_method(&iter, "next", "()Ljava/lang/Object;", &[]) + .expect("Failed to call next") + .l() + .expect("Failed to get object from next"); + let rrule = JavaModuleReverseIdentificationRule { + java_vm: self.java_vm.clone(), + rrule_jobject: Arc::new(env.new_global_ref(rrule_obj).expect( + "Failed to make an irule a global variable. Should not happen.", + )), + }; + rrules.push(Arc::new(rrule)); + } + } + } + rrules + } + + fn identification_step( + &self, + _decision_models: &Vec>, + _design_models: &Vec>, + ) -> IdentificationResult { + (vec![], vec![]) + } + + fn reverse_identification( + &self, + _solved_decision_model: &Vec>, + _design_model: &Vec>, + ) -> Vec> { + vec![] + } +} diff --git a/rust-bridge-ortools/Cargo.toml b/rust-bridge-ortools/Cargo.toml new file mode 100644 index 00000000..3c96d698 --- /dev/null +++ b/rust-bridge-ortools/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rust-bridge-ortools" +version.workspace = true +authors.workspace = true +edition.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +idesyde-core = { path = "../rust-core" } +idesyde-blueprints = { path = "../rust-blueprints" } +idesyde-common = { path = "../rust-common" } +cxx.workspace = true + +[build-dependencies] +cxx-build.workspace = true diff --git a/rust-bridge-ortools/include/solutions.hh b/rust-bridge-ortools/include/solutions.hh new file mode 100644 index 00000000..773cfa25 --- /dev/null +++ b/rust-bridge-ortools/include/solutions.hh @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +#include + +#include "ortools/base/logging.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/cp_model.pb.h" +#include "ortools/sat/cp_model_solver.h" +#include "ortools/util/sorted_interval_list.h" + +struct WorkloadDSEInput; + +void prepare_workload_dse_model( + WorkloadDSEInput const& input +); + diff --git a/rust-bridge-ortools/src/lib.rs b/rust-bridge-ortools/src/lib.rs new file mode 100644 index 00000000..926b1d6d --- /dev/null +++ b/rust-bridge-ortools/src/lib.rs @@ -0,0 +1,82 @@ +use std::collections::HashMap; + +use idesyde_common::models::PeriodicWorkloadToPartitionedSharedMultiCore; +use idesyde_core::{ + cast_dyn_decision_model, ExplorationBid, Explorer, Module, OpaqueDecisionModel, +}; + +#[cxx::bridge] +mod ffi { + + struct WorkloadDSEInput<'a> { + pe_targets: &'a [u32], + process_me_targets: &'a [u32], + buffer_me_targets: &'a [u32], + buffer_ce_targets: &'a [u32], + memory_limits: &'a [u64], + process_sizes: &'a [u64], + buffer_sizes: &'a [u64], + ce_max_slots: &'a [u32], + wcets: &'a [&'a [u32]], + pe_and_me_paths: &'a [&'a [&'a [u32]]], + } + + unsafe extern "C++" { + include!("rust-bridge-ortools/include/solutions.hh"); + + fn prepare_workload_dse_model(input: &WorkloadDSEInput); + + } +} + +struct ORToolExplorer; + +impl Explorer for ORToolExplorer { + fn unique_identifier(&self) -> String { + "ORToolExplorer".to_string() + } + + fn location_url(&self) -> Option { + None + } + + fn bid( + &self, + _other_explorers: &Vec>, + m: std::sync::Arc, + ) -> idesyde_core::ExplorationBid { + if let Some(m) = cast_dyn_decision_model!(m, PeriodicWorkloadToPartitionedSharedMultiCore) { + ExplorationBid { + can_explore: true, + is_exact: true, + competitiveness: 1.0, + target_objectives: "nUsedPEs", + additional_numeric_properties: HashMap::new(), + } + } + idesyde_core::ExplorationBid::impossible(self.unique_identifier().as_str()) + } + + fn explore( + &self, + m: std::sync::Arc, + _currrent_solutions: &std::collections::HashSet, + _exploration_configuration: idesyde_core::ExplorationConfiguration, + ) -> Box + Send + Sync + '_> { + if let Some(m) = cast_dyn_decision_model!(m, PeriodicWorkloadToPartitionedSharedMultiCore) { + } + Box::new(std::iter::empty()) + } +} + +struct ORToolsModule; + +impl Module for ORToolsModule { + fn unique_identifier(&self) -> String { + "ORToolsModule".to_string() + } + + fn explorers(&self) -> Vec> { + vec![Arc::new(ORToolExplorer)] + } +} diff --git a/rust-bridge-ortools/src/main.rs b/rust-bridge-ortools/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/rust-bridge-ortools/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/rust-bridge-ortools/src/solutions.cc b/rust-bridge-ortools/src/solutions.cc new file mode 100644 index 00000000..4e09f80d --- /dev/null +++ b/rust-bridge-ortools/src/solutions.cc @@ -0,0 +1,39 @@ +#include "rust-bridge-ortools/include/solutions.hh" +#include "rust-bridge-ortools/src/lib.rs.h" + +void prepare_workload_dse_model(WorkloadDSEInput const &input) { + + using namespace operations_research; + sat::CpModelBuilder cp_model; + + const IntVar x = cp_model.NewIntVar().WithName("x"); + // const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + // const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + // + // cp_model.AddNotEqual(x, y); + // + // Model model; + // + // int num_solutions = 0; + // model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + // LOG(INFO) << "Solution " << num_solutions; + // LOG(INFO) << " x = " << SolutionIntegerValue(r, x); + // LOG(INFO) << " y = " << SolutionIntegerValue(r, y); + // LOG(INFO) << " z = " << SolutionIntegerValue(r, z); + // num_solutions++; + // })); + // + // // Tell the solver to enumerate all solutions. + // SatParameters parameters; + // parameters.set_enumerate_all_solutions(true); + // model.Add(NewSatParameters(parameters)); + // const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + // + // LOG(INFO) << "Number of solutions found: " << num_solutions; +} + +int main() { + // operations_research::sat::SearchAllSolutionsSampleSat(); + + return 0; +} diff --git a/rust-common/src/irules.rs b/rust-common/src/irules.rs index bd0d2259..c135b408 100644 --- a/rust-common/src/irules.rs +++ b/rust-common/src/irules.rs @@ -3,7 +3,7 @@ use std::{ sync::Arc, }; -use idesyde_core::{DecisionModel, DesignModel, IdentificationResult}; +use idesyde_core::{DecisionModel, DesignModel, IdentificationResult, cast_dyn_decision_model}; use petgraph::{ visit::{Bfs, GraphBase, IntoNeighbors, IntoNodeIdentifiers, Visitable}, @@ -20,13 +20,13 @@ use crate::models::{ }; pub fn identify_partitioned_mem_mapped_multicore( - _design_models: &Vec>, - decision_models: &Vec>, + _design_models: &[Arc], + decision_models: &[Arc], ) -> IdentificationResult { let mut new_models = Vec::new(); let mut errors: Vec = Vec::new(); for m2 in decision_models { - if let Some(runt) = m2.downcast_ref::() { + if let Some(runt) = cast_dyn_decision_model!(m2, RuntimesAndProcessors) { let one_scheduler_per_proc = runt .processors .iter() @@ -51,7 +51,7 @@ pub fn identify_partitioned_mem_mapped_multicore( } if one_proc_per_scheduler && one_scheduler_per_proc { for m1 in decision_models { - if let Some(plat) = m1.downcast_ref::() { + if let Some(plat) = cast_dyn_decision_model!(m1, MemoryMappableMultiCore) { let potential = Arc::new(PartitionedMemoryMappableMulticore { hardware: plat.to_owned(), runtimes: runt.to_owned(), @@ -69,13 +69,13 @@ pub fn identify_partitioned_mem_mapped_multicore( } pub fn identify_partitioned_tiled_multicore( - _design_models: &Vec>, - decision_models: &Vec>, + _design_models: &[Arc], + decision_models: &[Arc], ) -> IdentificationResult { let mut new_models = Vec::new(); let mut errors: Vec = Vec::new(); for m2 in decision_models { - if let Some(runt) = m2.downcast_ref::() { + if let Some(runt) = cast_dyn_decision_model!(m2, RuntimesAndProcessors) { let same_number = runt.processors.len() == runt.runtimes.len(); let one_scheduler_per_proc = runt .processors @@ -104,7 +104,7 @@ pub fn identify_partitioned_tiled_multicore( } if same_number && one_proc_per_scheduler && one_scheduler_per_proc { for m1 in decision_models { - if let Some(plat) = m1.downcast_ref::() { + if let Some(plat) = cast_dyn_decision_model!(m1, TiledMultiCore) { let potential = Arc::new(PartitionedTiledMulticore { hardware: plat.to_owned(), runtimes: runt.to_owned(), @@ -132,14 +132,15 @@ pub fn identify_partitioned_tiled_multicore( /// 3. build the job graph parameters for each WCC, for each AnalysedSDFApplication, /// 4. return all the built AsynchronousAperiodicDataflow. pub fn identify_asynchronous_aperiodic_dataflow_from_sdf( - _design_models: &Vec>, - decision_models: &Vec>, + _design_models: &[Arc], + decision_models: &[Arc], ) -> IdentificationResult { let mut identified = Vec::new(); let mut errors: Vec = Vec::new(); for m in decision_models { - if let Some(analysed_sdf_application) = m.downcast_ref::() { + if let Some(analysed_sdf_application_val) = cast_dyn_decision_model!(m, AnalysedSDFApplication) { // build a temporary graph for analysis + let analysed_sdf_application = &analysed_sdf_application_val; let mut total_actors_graph: Graph<&str, usize, petgraph::Directed> = Graph::new(); let mut nodes = HashMap::new(); for a in &analysed_sdf_application.sdf_application.actors_identifiers { @@ -391,26 +392,26 @@ pub fn identify_asynchronous_aperiodic_dataflow_from_sdf( } pub fn identify_aperiodic_asynchronous_dataflow_to_partitioned_tiled_multicore( - _design_models: &Vec>, - decision_models: &Vec>, + _design_models: &[Arc], + decision_models: &[Arc], ) -> IdentificationResult { let mut identified: Vec> = Vec::new(); let mut errors: Vec = Vec::new(); if let Some(plat) = decision_models .iter() - .find_map(|x| x.downcast_ref::()) + .find_map(|x| cast_dyn_decision_model!(x, PartitionedTiledMulticore)) { if let Some(data) = decision_models .iter() - .find_map(|x| x.downcast_ref::()) + .find_map(|x| cast_dyn_decision_model!(x, InstrumentedComputationTimes)) { if let Some(mem_req) = decision_models .iter() - .find_map(|x| x.downcast_ref::()) + .find_map(|x| cast_dyn_decision_model!(x, InstrumentedMemoryRequirements)) { - let apps: Vec<&AperiodicAsynchronousDataflow> = decision_models + let apps: Vec = decision_models .iter() - .flat_map(|x| x.downcast_ref::()) + .flat_map(|x| cast_dyn_decision_model!(x, AperiodicAsynchronousDataflow)) .collect(); // check if all processes can be mapped let first_non_mappable = apps @@ -463,13 +464,13 @@ pub fn identify_aperiodic_asynchronous_dataflow_to_partitioned_tiled_multicore( /// This identification rule enriches an SDFApplication with the repetition vector and a PASS. pub fn identify_analyzed_sdf_from_common_sdf( - _design_models: &Vec>, - decision_models: &Vec>, + _design_models: &[Arc], + decision_models: &[Arc], ) -> IdentificationResult { let mut identified = Vec::new(); let mut msgs: Vec = Vec::new(); for m in decision_models { - if let Some(sdf_application) = m.downcast_ref::() { + if let Some(sdf_application) = cast_dyn_decision_model!(m, SDFApplication) { // build up the matrix that captures the topology matrix let mut topology_matrix: Vec> = Vec::new(); for (i, (src, dst)) in sdf_application @@ -549,26 +550,26 @@ pub fn identify_analyzed_sdf_from_common_sdf( } pub fn identify_aperiodic_asynchronous_dataflow_to_partitioned_mem_mappable_multicore( - _design_models: &Vec>, - decision_models: &Vec>, + _design_models: &[Arc], + decision_models: &[Arc], ) -> IdentificationResult { let mut identified: Vec> = Vec::new(); let mut errors: Vec = Vec::new(); if let Some(plat) = decision_models .iter() - .find_map(|x| x.downcast_ref::()) + .find_map(|x| cast_dyn_decision_model!(x, PartitionedMemoryMappableMulticore)) { if let Some(data) = decision_models .iter() - .find_map(|x| x.downcast_ref::()) + .find_map(|x| cast_dyn_decision_model!(x, InstrumentedComputationTimes)) { if let Some(mem_req) = decision_models .iter() - .find_map(|x| x.downcast_ref::()) + .find_map(|x| cast_dyn_decision_model!(x, InstrumentedMemoryRequirements)) { - let apps: Vec<&AperiodicAsynchronousDataflow> = decision_models + let apps: Vec = decision_models .iter() - .flat_map(|x| x.downcast_ref::()) + .flat_map(|x| cast_dyn_decision_model!(x, AperiodicAsynchronousDataflow)) .collect(); // check if all processes can be mapped let first_non_mappable = apps diff --git a/rust-common/src/lib.rs b/rust-common/src/lib.rs index 203585ab..ce04c48a 100644 --- a/rust-common/src/lib.rs +++ b/rust-common/src/lib.rs @@ -1,5 +1,4 @@ -use idesyde_blueprints::{StandaloneModule, StandaloneModuleBuilder}; -use idesyde_core::{decision_models_schemas_gen, opaque_to_model_gen}; +use idesyde_core::{decision_models_schemas_gen, opaque_to_model_gen, RustEmbeddedModule}; use models::{ AnalysedSDFApplication, AperiodicAsynchronousDataflow, AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore, @@ -8,48 +7,48 @@ use models::{ PartitionedTiledMulticore, RuntimesAndProcessors, SDFApplication, TiledMultiCore, }; use schemars::schema_for; -use std::collections::HashSet; +use std::{collections::HashSet, sync::Arc}; pub mod irules; pub mod models; -pub fn make_common_module() -> StandaloneModule { - StandaloneModuleBuilder::default() +pub fn make_module() -> RustEmbeddedModule { + RustEmbeddedModule::builder() .unique_identifier("CommonRustModule".to_string()) .identification_rules(vec![ - idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( + Arc::new(idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( irules::identify_partitioned_tiled_multicore, - ), - idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( + )), + Arc::new(idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( irules::identify_asynchronous_aperiodic_dataflow_from_sdf, - ), - idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( + )), + Arc::new(idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( irules::identify_aperiodic_asynchronous_dataflow_to_partitioned_tiled_multicore, - ), - idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( + )), + Arc::new(idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( irules::identify_partitioned_mem_mapped_multicore, - ), - idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( + )), + Arc::new(idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( irules::identify_aperiodic_asynchronous_dataflow_to_partitioned_mem_mappable_multicore, - ), - idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( + )), + Arc::new(idesyde_core::MarkedIdentificationRule::DecisionModelOnlyIdentificationRule( irules::identify_analyzed_sdf_from_common_sdf, - ) + )) ]) - .opaque_to_model(opaque_to_model_gen![ - SDFApplication, - AnalysedSDFApplication, - TiledMultiCore, - RuntimesAndProcessors, - PartitionedTiledMulticore, - AperiodicAsynchronousDataflow, - InstrumentedComputationTimes, - InstrumentedMemoryRequirements, - AperiodicAsynchronousDataflowToPartitionedTiledMulticore, - MemoryMappableMultiCore, - PartitionedMemoryMappableMulticore, - AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore - ]) + // .opaque_to_model(opaque_to_model_gen![ + // SDFApplication, + // AnalysedSDFApplication, + // TiledMultiCore, + // RuntimesAndProcessors, + // PartitionedTiledMulticore, + // AperiodicAsynchronousDataflow, + // InstrumentedComputationTimes, + // InstrumentedMemoryRequirements, + // AperiodicAsynchronousDataflowToPartitionedTiledMulticore, + // MemoryMappableMultiCore, + // PartitionedMemoryMappableMulticore, + // AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore + // ]) .decision_model_json_schemas(decision_models_schemas_gen![ SDFApplication, AnalysedSDFApplication, diff --git a/rust-common/src/main.rs b/rust-common/src/main.rs deleted file mode 100644 index 895e4c1e..00000000 --- a/rust-common/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use idesyde_blueprints::execute_standalone_module; -use idesyde_common::make_common_module; - -fn main() { - execute_standalone_module(make_common_module()); -} diff --git a/rust-common/src/models.rs b/rust-common/src/models.rs index 5dde3c7f..75b7ef6b 100644 --- a/rust-common/src/models.rs +++ b/rust-common/src/models.rs @@ -552,6 +552,12 @@ impl DecisionModel for AperiodicAsynchronousDataflowToPartitionedMemoryMappableM .iter() .map(|x| x.to_owned()), ); + elems.extend( + self.instrumented_memory_requirements + .part() + .iter() + .map(|x| x.to_owned()), + ); for (pe, sched) in &self.processes_to_runtime_scheduling { elems.insert(format!("{}={}:{}-{}:{}", "scheduling", pe, "", sched, "")); } @@ -571,3 +577,48 @@ impl DecisionModel for AperiodicAsynchronousDataflowToPartitionedMemoryMappableM elems } } + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, JsonSchema)] +pub struct PeriodicWorkloadToPartitionedSharedMultiCore { + pub workload: CommunicatingAndTriggeredReactiveWorkload, + pub platform: PartitionedMemoryMappableMulticore, + pub instrumented_computation_times: InstrumentedComputationTimes, + pub instrumented_memory_requirements: InstrumentedMemoryRequirements, + pub process_mapping: Vec<(String, String)>, + pub process_schedulings: Vec<(String, String)>, + pub channel_mappings: Vec<(String, String)>, + pub channel_slot_allocations: HashMap>>, + pub max_utilizations: HashMap, +} + +impl DecisionModel for PeriodicWorkloadToPartitionedSharedMultiCore { + impl_decision_model_standard_parts!(PeriodicWorkloadToPartitionedSharedMultiCore); + + fn part(&self) -> HashSet { + let mut elems: HashSet = HashSet::new(); + elems.extend(self.workload.part().iter().map(|x| x.to_owned())); + elems.extend(self.platform.part().iter().map(|x| x.to_owned())); + elems.extend( + self.instrumented_computation_times + .part() + .iter() + .map(|x| x.to_owned()), + ); + elems.extend( + self.instrumented_memory_requirements + .part() + .iter() + .map(|x| x.to_owned()), + ); + for (pe, sched) in &self.process_schedulings { + elems.insert(format!("{}={}:{}-{}:{}", "scheduling", pe, "", sched, "")); + } + for (pe, mem) in &self.process_mapping { + elems.insert(format!("{}={}:{}-{}:{}", "mapping", pe, "", mem, "")); + } + for (buf, mem) in &self.channel_mappings { + elems.insert(format!("{}={}:{}-{}:{}", "mapping", buf, "", mem, "")); + } + elems + } +} diff --git a/rust-core/src/lib.rs b/rust-core/src/lib.rs index 95190b0a..7d922826 100644 --- a/rust-core/src/lib.rs +++ b/rust-core/src/lib.rs @@ -3,8 +3,12 @@ pub mod macros; use std::{ collections::{HashMap, HashSet}, hash::Hash, + ops::Add, path::Path, - sync::{mpsc::Receiver, Arc}, + sync::{ + mpsc::{Receiver, Sender}, + Arc, Mutex, + }, time::{Duration, Instant}, }; @@ -15,6 +19,40 @@ use sha2::{Digest, Sha512}; use std::cmp::Ordering; use url::Url; +/// A simple structure to contain a result and accumulate information regarding its computation +#[derive( + Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_builder::Builder, +)] +pub struct LoggedResult { + pub result: T, + #[builder(default = "Vec::new()")] + pub info: Vec, + #[builder(default = "Vec::new()")] + pub warn: Vec, + #[builder(default = "Vec::new()")] + pub err: Vec, + #[builder(default = "Vec::new()")] + pub debug: Vec, +} + +impl LoggedResult { + pub fn builder() -> LoggedResultBuilder { + LoggedResultBuilder::default() + } +} + +impl From for LoggedResult { + fn from(value: T) -> Self { + LoggedResult { + result: value, + info: Vec::new(), + warn: Vec::new(), + err: Vec::new(), + debug: Vec::new(), + } + } +} + /// The trait/interface for a design model in the design space identification methodology, as /// defined in [1]. /// @@ -310,6 +348,58 @@ impl Hash for dyn DecisionModel { pub type IdentificationResult = (Vec>, Vec); +pub type ReverseIdentificationResult = (Vec>, Vec); + +pub trait IdentificationRuleLike: Send + Sync { + fn identify( + &self, + design_models: &[Arc], + decision_models: &[Arc], + ) -> IdentificationResult; + + fn uses_design_models(&self) -> bool { + true + } + + fn uses_decision_models(&self) -> bool { + true + } + + fn uses_specific_decision_models(&self) -> Option> { + None + } +} + +impl IdentificationRuleLike for Arc { + fn identify( + &self, + design_models: &[Arc], + decision_models: &[Arc], + ) -> IdentificationResult { + self.as_ref().identify(design_models, decision_models) + } + + fn uses_design_models(&self) -> bool { + self.as_ref().uses_design_models() + } + + fn uses_decision_models(&self) -> bool { + self.as_ref().uses_decision_models() + } + + fn uses_specific_decision_models(&self) -> Option> { + self.as_ref().uses_specific_decision_models() + } +} + +pub trait ReverseIdentificationRuleLike: Send + Sync { + fn reverse_identify( + &self, + decision_models: &[Arc], + design_models: &[Arc], + ) -> ReverseIdentificationResult; +} + pub type IdentificationRule = fn(&Vec>, &Vec>) -> IdentificationResult; @@ -317,22 +407,68 @@ pub type ReverseIdentificationRule = fn(&Vec>, &Vec>) -> Vec>; #[derive(Debug, Clone, PartialEq, Eq)] -pub enum MarkedIdentificationRule { - DesignModelOnlyIdentificationRule(IdentificationRule), - DecisionModelOnlyIdentificationRule(IdentificationRule), - SpecificDecisionModelIdentificationRule(HashSet, IdentificationRule), - GenericIdentificationRule(IdentificationRule), +pub enum MarkedIdentificationRule where +T : Fn(&[Arc], &[Arc]) -> IdentificationResult + Send + Sync { + DesignModelOnlyIdentificationRule(T), + DecisionModelOnlyIdentificationRule(T), + SpecificDecisionModelIdentificationRule(HashSet, T), + GenericIdentificationRule(T), +} + +impl IdentificationRuleLike for MarkedIdentificationRule +where T : Fn(&[Arc], &[Arc]) -> IdentificationResult + Send + Sync { + fn identify( + &self, + design_models: &[Arc], + decision_models: &[Arc], + ) -> IdentificationResult { + match self { + MarkedIdentificationRule::DesignModelOnlyIdentificationRule(r) => r(design_models, decision_models), + MarkedIdentificationRule::DecisionModelOnlyIdentificationRule(r) => r(design_models, decision_models), + MarkedIdentificationRule::SpecificDecisionModelIdentificationRule(_, r) => r(design_models, decision_models), + MarkedIdentificationRule::GenericIdentificationRule(r) => r(design_models, decision_models) + } + } + + fn uses_design_models(&self) -> bool { + match self { + MarkedIdentificationRule::DesignModelOnlyIdentificationRule(_) => true, + MarkedIdentificationRule::DecisionModelOnlyIdentificationRule(_) => false, + MarkedIdentificationRule::SpecificDecisionModelIdentificationRule(_, _) => false, + MarkedIdentificationRule::GenericIdentificationRule(_) => true, + } + } + + fn uses_decision_models(&self) -> bool { + match self { + MarkedIdentificationRule::DesignModelOnlyIdentificationRule(_) => false, + MarkedIdentificationRule::DecisionModelOnlyIdentificationRule(_) => true, + MarkedIdentificationRule::SpecificDecisionModelIdentificationRule(_, _) => true, + MarkedIdentificationRule::GenericIdentificationRule(_) => true, + } + } + + fn uses_specific_decision_models(&self) -> Option> { + match self { + MarkedIdentificationRule::DesignModelOnlyIdentificationRule(_) => None, + MarkedIdentificationRule::DecisionModelOnlyIdentificationRule(_) => None, + MarkedIdentificationRule::SpecificDecisionModelIdentificationRule(x, _) => Some(x.iter().map(|x| x.to_string()).collect()), + MarkedIdentificationRule::GenericIdentificationRule(_) => None, + } + } + + } #[derive(Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_builder::Builder)] // #[builder(setter(each(name = "target_objectives")))] pub struct ExplorationConfiguration { - pub max_sols: u64, + pub max_sols: i64, pub total_timeout: u64, pub improvement_timeout: u64, pub time_resolution: u64, pub memory_resolution: u64, - pub improvement_iterations: u64, + pub improvement_iterations: i64, pub strict: bool, pub target_objectives: HashSet, } @@ -444,12 +580,17 @@ impl PartialOrd for ExplorationSolution { /// An exploration bidding captures the characteristics that an explorer /// might display when exploring a decision model. -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, derive_builder::Builder)] pub struct ExplorationBid { + #[builder(default = "false")] pub can_explore: bool, + #[builder(default = "false")] pub is_exact: bool, + #[builder(default = "1.0")] pub competitiveness: f32, + #[builder(default = "HashSet::new()")] pub target_objectives: HashSet, + #[builder(default = "HashMap::new()")] pub additional_numeric_properties: HashMap, } @@ -458,7 +599,7 @@ impl ExplorationBid { serde_json::from_str(s).ok() } - pub fn impossible(explorer_id: &str) -> ExplorationBid { + pub fn impossible() -> ExplorationBid { ExplorationBid { can_explore: false, is_exact: false, @@ -467,6 +608,10 @@ impl ExplorationBid { additional_numeric_properties: HashMap::new(), } } + + pub fn builder() -> ExplorationBidBuilder { + ExplorationBidBuilder::default() + } } impl Hash for ExplorationBid { @@ -547,20 +692,16 @@ pub trait Explorer: Downcast + Send + Sync { /// Give information about the exploration capabilities of this /// explorer for a decision model given that other explorers are present. - fn bid( - &self, - _other_explorers: &Vec>, - _m: Arc, - ) -> ExplorationBid { - ExplorationBid::impossible(&self.unique_identifier()) + fn bid(&self, _m: Arc) -> ExplorationBid { + ExplorationBid::impossible() } fn explore( &self, _m: Arc, _currrent_solutions: &HashSet, _exploration_configuration: ExplorationConfiguration, - ) -> Box + Send + Sync + '_> { - Box::new(std::iter::empty()) + ) -> Arc + Send + Sync>> { + Arc::new(Mutex::new(std::iter::empty())) } } impl_downcast!(Explorer); @@ -589,12 +730,8 @@ impl Explorer for Arc { self.as_ref().location_url() } - fn bid( - &self, - _other_explorers: &Vec>, - _m: Arc, - ) -> ExplorationBid { - self.as_ref().bid(_other_explorers, _m) + fn bid(&self, _m: Arc) -> ExplorationBid { + self.as_ref().bid(_m) } fn explore( @@ -602,7 +739,7 @@ impl Explorer for Arc { _m: Arc, _currrent_solutions: &HashSet, _exploration_configuration: ExplorationConfiguration, - ) -> Box + Send + Sync + '_> { + ) -> Arc + Send + Sync>> { self.as_ref() .explore(_m, _currrent_solutions, _exploration_configuration) } @@ -629,6 +766,9 @@ pub struct OpaqueDecisionModel { } impl OpaqueDecisionModel { + pub fn builder() -> OpaqueDecisionModelBuilder { + OpaqueDecisionModelBuilder::default() + } pub fn from_json_str(s: &str) -> Result { serde_json::from_str(s) } @@ -741,6 +881,9 @@ pub struct OpaqueDesignModel { } impl OpaqueDesignModel { + pub fn builder() -> OpaqueDesignModelBuilder { + OpaqueDesignModelBuilder::default() + } pub fn from_path_str(s: &str) -> OpaqueDesignModel { let path = Path::new(s); return path.into(); @@ -878,45 +1021,23 @@ impl From> for OpaqueDesignModel { } } -/// This trait is wrapper around the normal iteration to create a "session" -/// for identification modules. Via this, we can do more advanced things -/// that would otherwise be impossible with a simple function call or iterator, -/// like caching the decision or design models to not send them unnecesarily remotely. -/// -/// Prefer to use `next_with_models` over `next` as it inserts the required models as -/// necessary in the internal state of this iterator. -pub trait IdentificationIterator: Iterator + Sync { - fn next_with_models( - &mut self, - _decision_models: &Vec>, - _design_models: &Vec>, - ) -> Option { - return None; - } - - // This method collect messages possibly produced during the identification session, - // e.g. errors, information or warnings, and returns it to the caller. - // - // The messages come in a (level_string, content_string) format. - // - // The trait shoud ensure that consumed messages are destroyed from the iterator. - // fn collect_messages(&mut self) -> Vec<(String, String)> { - // vec![] - // } -} - -/// A simple empty unit struct for an empty iterator -pub struct EmptyIdentificationIterator {} - -impl Iterator for EmptyIdentificationIterator { - type Item = IdentificationResult; +impl PartialEq for OpaqueDesignModel { + fn eq(&self, other: &OpaqueDesignModel) -> bool { + self.category == other.category && self.elements == other.elements + } +} - fn next(&mut self) -> Option { - None +impl Eq for OpaqueDesignModel {} + +impl Hash for OpaqueDesignModel { + fn hash(&self, state: &mut H) { + self.category.hash(state); + for x in &self.elements { + x.hash(state); + } } } -impl IdentificationIterator for EmptyIdentificationIterator {} /// Identification modules are a thin layer on top of identification rules that facilitates treating /// (reverse) identification rules within the orchestration process or remotely in the same fashion. @@ -929,13 +1050,27 @@ pub trait Module: Send + Sync { fn explorers(&self) -> Vec> { Vec::new() } + fn identification_rules(&self) -> Vec> { + vec![] + } + fn reverse_identification_rules(&self) -> Vec> { + vec![] + } fn identification_step( &self, - _decision_models: &Vec>, - _design_models: &Vec>, + decision_models: &Vec>, + design_models: &Vec>, ) -> IdentificationResult { - (vec![], vec![]) + let mut identified = Vec::new(); + let mut messages = Vec::new(); + for irule in self.identification_rules() { + let (i, m) = irule.identify(design_models, decision_models); + identified.extend(i); + messages.extend(m); + } + (identified, messages) } + fn reverse_identification( &self, _solved_decision_model: &Vec>, @@ -960,14 +1095,51 @@ impl Hash for dyn Module { } } +#[derive(Clone, Builder)] +pub struct RustEmbeddedModule { + unique_identifier: String, + #[builder(default = "Vec::new()")] + explorers: Vec>, + #[builder(default = "vec![]")] + identification_rules: Vec>, + #[builder(default = "vec![]")] + reverse_identification_rules: Vec>, + #[builder(default = "HashSet::new()")] + pub decision_model_json_schemas: HashSet, +} + +impl RustEmbeddedModule { + pub fn builder() -> RustEmbeddedModuleBuilder { + RustEmbeddedModuleBuilder::default() + } +} + +impl Module for RustEmbeddedModule { + fn unique_identifier(&self) -> String { + self.unique_identifier.to_owned() + } + + fn explorers(&self) -> Vec> { + self.explorers.to_owned() + } + + fn identification_rules(&self) -> Vec> { + self.identification_rules.to_owned() + } + + fn reverse_identification_rules(&self) -> Vec> { + self.reverse_identification_rules.to_owned() + } +} + /// This iterator is able to get a handful of explorers + decision models combination /// and make the exploration cooperative. It does so by exchanging the solutions /// found between explorers so that the explorers almost always with the latest approximate Pareto set /// update between themselves. pub struct CombinedExplorerIterator { - sol_channels: Vec>, + sol_channels: Vec>, is_exact: Vec, - finish_request_channels: Vec>, + finish_request_channels: Vec>, duration_left: Option, _handles: Vec>, } @@ -993,8 +1165,8 @@ impl CombinedExplorerIterator { currrent_solutions: &HashSet, exploration_configuration: ExplorationConfiguration, ) -> CombinedExplorerIterator { - let mut sol_channels: Vec> = Vec::new(); - let mut completed_channels: Vec> = Vec::new(); + let mut sol_channels: Vec> = Vec::new(); + let mut completed_channels: Vec> = Vec::new(); let mut handles: Vec> = Vec::new(); for (e, m) in explorers_and_models { let (sc, cc, h) = explore_non_blocking( @@ -1086,7 +1258,7 @@ pub struct MultiLevelCombinedExplorerIterator { Arc>, ), solutions: HashSet, - converged_to_last_level: bool, + // converged_to_last_level: bool, start: Instant, } @@ -1134,7 +1306,14 @@ impl Iterator for MultiLevelCombinedExplorerIterator { } }); } - return Some(solution); + // return if the solution is not dominated + if self + .solutions + .iter() + .all(|cur_sol| solution.partial_cmp(cur_sol) != Some(Ordering::Greater)) + { + return Some(solution); + } } Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => { if let (Some(prev_level), _) = &self.levels_stream { @@ -1211,84 +1390,84 @@ impl Iterator for MultiLevelCombinedExplorerIterator { } } -pub fn explore_cooperatively_simple( - explorers_and_models: &Vec<(Arc, Arc)>, - currrent_solutions: &HashSet, - exploration_configuration: ExplorationConfiguration, - // solution_inspector: F, -) -> MultiLevelCombinedExplorerIterator { - let combined_explorer = CombinedExplorerIterator::start( - &explorers_and_models, - &currrent_solutions, - exploration_configuration.to_owned(), - ); - let (sender, receiver) = std::sync::mpsc::channel::(); - // move the data structures to contain new explorers - let levels_stream = (None, Arc::new(receiver)); - // let levels_tuple = (None, combined_explorer); - std::thread::spawn(move || { - for sol in combined_explorer { - match sender.send(sol) { - Ok(_) => {} - Err(_) => {} - }; - } - }); - MultiLevelCombinedExplorerIterator { - explorers_and_models: explorers_and_models.clone(), - solutions: currrent_solutions.clone(), - exploration_configuration: exploration_configuration.to_owned(), - // levels: vec![CombinedExplorerIterator::start_with_exact( - // explorers_and_models, - // &biddings.iter().map(|b| b.is_exact).collect(), - // currrent_solutions, - // exploration_configuration.to_owned(), - // )], - levels_stream, - converged_to_last_level: false, - start: Instant::now(), - } -} - -pub fn explore_cooperatively( - explorers_and_models: &Vec<(Arc, Arc)>, - biddings: &Vec, - currrent_solutions: &HashSet, - exploration_configuration: ExplorationConfiguration, - // solution_inspector: F, -) -> MultiLevelCombinedExplorerIterator { - let combined_explorer = CombinedExplorerIterator::start( - &explorers_and_models, - &currrent_solutions, - exploration_configuration.to_owned(), - ); - let (sender, receiver) = std::sync::mpsc::channel::(); - // move the data structures to contain new explorers - let levels_stream = (None, Arc::new(receiver)); - // let levels_tuple = (None, combined_explorer); - std::thread::spawn(move || { - for sol in combined_explorer { - match sender.send(sol) { - Ok(_) => {} - Err(_) => {} - }; - } - }); - MultiLevelCombinedExplorerIterator { - explorers_and_models: explorers_and_models.clone(), - solutions: currrent_solutions.clone(), - exploration_configuration: exploration_configuration.to_owned(), - // levels: vec![CombinedExplorerIterator::start_with_exact( - // explorers_and_models, - // &biddings.iter().map(|b| b.is_exact).collect(), - // currrent_solutions, - // exploration_configuration.to_owned(), - // )], - levels_stream, - converged_to_last_level: false, - start: Instant::now(), - } -} +// pub fn explore_cooperatively_simple( +// explorers_and_models: &Vec<(Arc, Arc)>, +// currrent_solutions: &HashSet, +// exploration_configuration: ExplorationConfiguration, +// // solution_inspector: F, +// ) -> MultiLevelCombinedExplorerIterator { +// let combined_explorer = CombinedExplorerIterator::start( +// &explorers_and_models, +// &currrent_solutions, +// exploration_configuration.to_owned(), +// ); +// let (sender, receiver) = std::sync::mpsc::channel::(); +// // move the data structures to contain new explorers +// let levels_stream = (None, Arc::new(receiver)); +// // let levels_tuple = (None, combined_explorer); +// std::thread::spawn(move || { +// for sol in combined_explorer { +// match sender.send(sol) { +// Ok(_) => {} +// Err(_) => {} +// }; +// } +// }); +// MultiLevelCombinedExplorerIterator { +// explorers_and_models: explorers_and_models.clone(), +// solutions: currrent_solutions.clone(), +// exploration_configuration: exploration_configuration.to_owned(), +// // levels: vec![CombinedExplorerIterator::start_with_exact( +// // explorers_and_models, +// // &biddings.iter().map(|b| b.is_exact).collect(), +// // currrent_solutions, +// // exploration_configuration.to_owned(), +// // )], +// levels_stream, +// // converged_to_last_level: false, +// start: Instant::now(), +// } +// } + +// pub fn explore_cooperatively( +// explorers_and_models: &Vec<(Arc, Arc)>, +// _biddings: &Vec, +// currrent_solutions: &HashSet, +// exploration_configuration: ExplorationConfiguration, +// // solution_inspector: F, +// ) -> MultiLevelCombinedExplorerIterator { +// let combined_explorer = CombinedExplorerIterator::start( +// &explorers_and_models, +// &currrent_solutions, +// exploration_configuration.to_owned(), +// ); +// let (sender, receiver) = std::sync::mpsc::channel::(); +// // move the data structures to contain new explorers +// let levels_stream = (None, Arc::new(receiver)); +// // let levels_tuple = (None, combined_explorer); +// std::thread::spawn(move || { +// for sol in combined_explorer { +// match sender.send(sol) { +// Ok(_) => {} +// Err(_) => {} +// }; +// } +// }); +// MultiLevelCombinedExplorerIterator { +// explorers_and_models: explorers_and_models.clone(), +// solutions: currrent_solutions.clone(), +// exploration_configuration: exploration_configuration.to_owned(), +// // levels: vec![CombinedExplorerIterator::start_with_exact( +// // explorers_and_models, +// // &biddings.iter().map(|b| b.is_exact).collect(), +// // currrent_solutions, +// // exploration_configuration.to_owned(), +// // )], +// levels_stream, +// // converged_to_last_level: false, +// start: Instant::now(), +// } +// } pub fn compute_dominant_bidding<'a, I>(biddings: I) -> Option<(usize, ExplorationBid)> where @@ -1386,8 +1565,8 @@ pub fn explore_non_blocking( currrent_solutions: &HashSet, exploration_configuration: ExplorationConfiguration, ) -> ( - std::sync::mpsc::Receiver, - std::sync::mpsc::Sender, + Receiver, + Sender, std::thread::JoinHandle<()>, ) where @@ -1403,14 +1582,17 @@ where if let Ok(true) = completed_rx.recv_timeout(std::time::Duration::from_millis(300)) { return (); } - for new_solution in this_explorer.explore( - this_decision_model, - &prev_sols, - exploration_configuration.to_owned(), - ) { - match solution_tx.send(new_solution) { - Ok(_) => {} - Err(_) => return (), + if let Ok(mut iter) = this_explorer + .explore( + this_decision_model, + &prev_sols, + exploration_configuration.to_owned(), + ) + .lock() + { + match iter.next().and_then(|x| solution_tx.send(x).ok()) { + Some(_) => {} + None => return (), }; } }); @@ -1453,6 +1635,26 @@ pub fn pareto_dominance_partial_cmp( } } -pub fn empty_identification_iter() -> EmptyIdentificationIterator { - EmptyIdentificationIterator {} +pub fn merge_identification_results( + result1: IdentificationResult, + result2: IdentificationResult, +) -> IdentificationResult { + let (models1, msgs1) = result1; + let (models2, msgs2) = result2; + let mut models = Vec::new(); + models.extend(models1.into_iter()); + for m in models2 { + if !models.contains(&m) { + models.push(m); + } + } + // models.extend(models2.into_iter().filter(|m| !models.contains(m))); + let mut msgs = Vec::new(); + msgs.extend(msgs1.into_iter()); + for msg in msgs2 { + if !msgs.contains(&msg) { + msgs.push(msg); + } + } + (models, msgs) } diff --git a/rust-core/src/macros.rs b/rust-core/src/macros.rs index 2ae5fc3e..66451719 100644 --- a/rust-core/src/macros.rs +++ b/rust-core/src/macros.rs @@ -62,3 +62,42 @@ macro_rules! impl_decision_model_standard_parts { } }; } + +/// This macro takes a reference to a DecisionModel trait object +/// and tries to downcast to a specific decision model. +/// +/// The macro generates is smart enough to +/// _also_ decode decision models from OpaqueDecisionModel. +/// Hence, this macro is essentially a shortcut to all the means a +/// non-specific decision model can be made specific. +/// +/// So, if you call: +/// +/// cast_dyn_decision_model!(m ,t) +/// +/// where `m` is an `&dyn DecisionModel` or equivalent, e.g. `Arc`, +/// and `t` is a `DecisionModel` type, then the resulting will be `Option>`. +#[macro_export] +macro_rules! cast_dyn_decision_model { + ($b:ident,$x:ty) => { + $b.downcast_ref::() + .and_then(|opaque| { + if idesyde_core::DecisionModel::category(opaque).as_str() == stringify!($x) { + idesyde_core::DecisionModel::body_as_cbor(opaque) + .and_then(|b| ciborium::from_reader::<$x, &[u8]>(b.as_slice()).ok()) + .or_else(|| { + idesyde_core::DecisionModel::body_as_json(opaque) + .and_then(|j| serde_json::from_str::<$x>(&j).ok()) + }) + .or_else(|| { + idesyde_core::DecisionModel::body_as_msgpack(opaque) + .and_then(|j| rmp_serde::from_slice::<$x>(&j).ok()) + }) + // .map(|m| std::sync::Arc::new(m) as Arc<$x>) + } else { + None as Option<$x> + } + }) + .or_else(|| $b.downcast_ref::<$x>().map(|x| x.to_owned())) + }; +} diff --git a/rust-orchestration/Cargo.toml b/rust-orchestration/Cargo.toml index ef5d7dd2..26495585 100644 --- a/rust-orchestration/Cargo.toml +++ b/rust-orchestration/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true idesyde-core = { path = "../rust-core" } idesyde-common = { path = "../rust-common" } idesyde-blueprints = { path = "../rust-blueprints" } +idesyde-bridge-java = { path = "../rust-bridge-java" } clap = { workspace = true } env_logger.workspace = true log.workspace = true @@ -20,5 +21,5 @@ reqwest.workspace = true tungstenite.workspace = true url.workspace = true derive_builder.workspace = true -reqwest-eventsource.workspace = true base64.workspace = true +rusqlite.workspace = true diff --git a/rust-orchestration/src/exploration.rs b/rust-orchestration/src/exploration.rs index 78ca641d..d78aad2e 100644 --- a/rust-orchestration/src/exploration.rs +++ b/rust-orchestration/src/exploration.rs @@ -1,11 +1,12 @@ use std::{ cmp::Ordering, - collections::{HashMap, HashSet}, - io::BufRead, - io::BufReader, + collections::{HashMap, HashSet, VecDeque}, + io::{BufRead, BufReader}, path::PathBuf, process::{Child, Stdio}, + rc::Rc, sync::{Arc, Mutex}, + time::{Duration, Instant}, }; use derive_builder::Builder; @@ -15,9 +16,12 @@ use idesyde_core::{ ExplorationSolution, Explorer, Module, OpaqueDecisionModel, }; use log::{debug, warn}; -use serde::{Deserialize, Serialize}; +use reqwest::blocking::multipart::Form; +use serde::{de, Deserialize, Serialize}; use url::Url; +use rayon::prelude::*; + #[derive(Deserialize, Serialize, PartialEq, Clone)] pub struct ExplorerBidding { explorer_unique_identifier: String, @@ -161,20 +165,7 @@ impl Explorer for ExternalExplorer { Some(self.url.to_owned()) } - fn bid( - &self, - _explorers: &Vec>, - m: Arc, - ) -> ExplorationBid { - // let mut form = reqwest::blocking::multipart::Form::new(); - // form = form.part( - // format!("decisionModel"), - // reqwest::blocking::multipart::Part::text( - // OpaqueDecisionModel::from(m) - // .to_json() - // .expect("Failed to make Json out of opaque decision model. Should never fail."), - // ), - // ); + fn bid(&self, m: Arc) -> ExplorationBid { let model_hash = m.global_sha2_hash(); let exists = self .url @@ -187,11 +178,14 @@ impl Explorer for ExternalExplorer { if !exists { // debug!("{} is not in cache for {}. Adding it with {:?}.", m.category(), self.unique_identifier(), m.global_sha2_hash()); if let Ok(json_str) = OpaqueDecisionModel::from(m).to_json() { - if let Ok(r) = self.client - .put(self.url.join("/decision/cache/add").unwrap()) - .body(json_str) - .send() { + if let Ok(r) = self + .client + .post(self.url.join("/decision/cache/add").unwrap()) + .multipart(Form::new().text("decisionModel", json_str)) + .send() + { // debug!("Added decision model to cache: {:?}", r.bytes().unwrap()); + debug!("{}", r.text().unwrap()); }; } } @@ -228,7 +222,7 @@ impl Explorer for ExternalExplorer { } } } - ExplorationBid::impossible(&self.unique_identifier()) + ExplorationBid::impossible() } fn explore( @@ -236,7 +230,7 @@ impl Explorer for ExternalExplorer { m: Arc, currrent_solutions: &HashSet, exploration_configuration: ExplorationConfiguration, - ) -> Box + Send + Sync + '_> { + ) -> Arc + Send + Sync>> { let mut mut_url = self.url.clone(); if let Err(_) = mut_url.set_scheme("ws") { warn!( @@ -278,7 +272,7 @@ impl Explorer for ExternalExplorer { warn!("Failed to send exploration request to {} for exploration. Exploration is likely to fail.", self.unique_identifier()); debug!("Message was: {}", e.to_string()); }; - return Box::new(ExternalExplorerSolutionIter::new(ws)); + return Arc::new(Mutex::new(ExternalExplorerSolutionIter::new(ws))); } } else { warn!("Failed to open exploration connetion. Trying to proceed anyway."); @@ -360,7 +354,153 @@ impl Explorer for ExternalExplorer { // ); // } // } - Box::new(std::iter::empty()) + Arc::new(Mutex::new(std::iter::empty())) + } +} + +/// This iterator is able to get a handful of explorers + decision models combination +/// and make the exploration cooperative. It does so by exchanging the solutions +/// found between explorers so that the explorers almost always with the latest approximate Pareto set +/// update between themselves. +#[derive(Clone)] +pub struct CombinedExplorerIterator2 { + iterators: Vec + Send + Sync>>>, + is_exact: Vec, + duration_left: Option, + solutions_left: Option, +} + +impl CombinedExplorerIterator2 { + pub fn create( + explorers_and_models: &[(Arc, Arc)], + biddings: &[ExplorationBid], + solutions: &HashSet, + exploration_configuration: &ExplorationConfiguration, + solutions_found: u64, + ) -> Self { + let new_duration = if exploration_configuration.total_timeout > 0 { + Some(Duration::from_secs( + exploration_configuration.total_timeout - Instant::now().elapsed().as_secs(), + )) + } else { + None + }; + let new_solution_limit = if exploration_configuration.max_sols >= 0 { + if exploration_configuration.max_sols as u64 > solutions_found { + Some(exploration_configuration.max_sols as u64 - solutions_found) + } else { + Some(0) + } + } else { + None + }; + CombinedExplorerIterator2 { + iterators: explorers_and_models + .iter() + .map(|(e, m)| e.explore(m.to_owned(), solutions, exploration_configuration.clone())) + .collect(), + is_exact: biddings.iter().map(|b| b.is_exact).collect(), + duration_left: new_duration, + solutions_left: new_solution_limit, + } + } +} + +impl Iterator for CombinedExplorerIterator2 { + type Item = ExplorationSolution; + + fn next(&mut self) -> Option { + let start = Instant::now(); + self.duration_left = self.duration_left.map(|d| { + if d >= start.elapsed() { + d - start.elapsed() + } else { + Duration::ZERO + } + }); + if self.solutions_left.map(|x| x > 0).unwrap_or(true) + && self + .duration_left + .map(|d| d > Duration::ZERO) + .unwrap_or(true) + { + return self + .iterators + .par_iter_mut() + .enumerate() + .map(|(i, iter_mutex)| { + if let Ok(mut iter) = iter_mutex.lock() { + return (i, iter.next()); + } + (i, None) + }) + .take_any_while(|(i, x)| x.is_some() || !self.is_exact[*i]) + .flat_map(|(_, x)| x) + .find_any(|_| true); + } + None + } +} + +pub struct MultiLevelCombinedExplorerIterator2 { + explorers_and_models: Vec<(Arc, Arc)>, + biddings: Vec, + exploration_configuration: ExplorationConfiguration, + // levels: Vec, + // levels_tuple: (Option, CombinedExplorerIterator), + iterators: VecDeque, + solutions: HashSet, + num_found: u64, + // converged_to_last_level: bool, + start: Instant, +} + +impl Iterator for MultiLevelCombinedExplorerIterator2 { + type Item = ExplorationSolution; + + fn next(&mut self) -> Option { + if self.exploration_configuration.total_timeout > 0 + && self.start.elapsed() + > Duration::from_secs(self.exploration_configuration.total_timeout) + { + return None; + } + if self.iterators.len() > 2 { + self.iterators.pop_back(); + } + if let Some(current_level) = self.iterators.front_mut() { + if let Some(non_dominated) = current_level.find(|x| { + !self + .solutions + .iter() + .any(|s| s.partial_cmp(&x) == Some(Ordering::Less)) + }) { + self.num_found += 1; + self.solutions.insert(non_dominated.clone()); + let sol_dominates = self + .solutions + .iter() + .any(|cur_sol| non_dominated.partial_cmp(cur_sol) == Some(Ordering::Less)); + if sol_dominates { + self.solutions.retain(|cur_sol| { + non_dominated.partial_cmp(cur_sol) != Some(Ordering::Less) + }); + let mut new_iterator = CombinedExplorerIterator2::create( + self.explorers_and_models.as_slice(), + self.biddings.as_slice(), + &self.solutions, + &self.exploration_configuration, + self.num_found, + ); + self.iterators.push_front(new_iterator); + }; + return Some(non_dominated); + } else { + self.iterators.pop_front(); + return self.next(); + } + }; + None } } @@ -375,3 +515,33 @@ pub fn compute_pareto_solutions(sols: Vec) -> Vec, Arc)], + biddings: &[ExplorationBid], + currrent_solutions: &HashSet, + exploration_configuration: &ExplorationConfiguration, + // solution_inspector: F, +) -> MultiLevelCombinedExplorerIterator2 { + let combined_explorer = CombinedExplorerIterator2::create( + explorers_and_models, + biddings, + currrent_solutions, + exploration_configuration, + 0, + ); + let mut deque = VecDeque::new(); + deque.push_front(combined_explorer); + MultiLevelCombinedExplorerIterator2 { + explorers_and_models: explorers_and_models + .iter() + .map(|(e, m)| (e.to_owned(), m.to_owned())) + .collect(), + biddings: biddings.to_owned(), + solutions: currrent_solutions.clone(), + exploration_configuration: exploration_configuration.to_owned(), + iterators: deque, + start: Instant::now(), + num_found: 0, + } +} diff --git a/rust-orchestration/src/identification.rs b/rust-orchestration/src/identification.rs index c5438ec8..a17a9165 100644 --- a/rust-orchestration/src/identification.rs +++ b/rust-orchestration/src/identification.rs @@ -1,36 +1,19 @@ use std::{ - collections::HashSet, - f32::consts::E, net::TcpStream, - ops::Index, - sync::{Arc, Mutex}, - time::Duration, + sync::Arc, }; use idesyde_core::{ - DecisionModel, DesignModel, IdentificationIterator, IdentificationResult, Module, - OpaqueDecisionModel, OpaqueDesignModel, + merge_identification_results, DecisionModel, DesignModel, + IdentificationResult, IdentificationRuleLike, Module, OpaqueDecisionModel, OpaqueDesignModel, }; use log::debug; +use rusqlite::params; use tungstenite::WebSocket; use rayon::prelude::*; -// impl HttpServerLike for ExternalServerIdentificationModule { -// fn get_client(&self) -> Arc { -// self.client.clone() -// } - -// fn get_address(&self) -> std::net::IpAddr { -// self.address.to_owned() -// } - -// fn get_port(&self) -> usize { -// self.port -// } -// } - pub struct ExternalServerIdentifiticationIterator { design_models: Vec>, decision_models: Vec>, @@ -148,39 +131,6 @@ impl Iterator for ExternalServerIdentifiticationIterator { } } -impl IdentificationIterator for ExternalServerIdentifiticationIterator { - fn next_with_models( - &mut self, - decision_models: &Vec>, - design_models: &Vec>, - ) -> Option { - self.decision_models_to_upload.extend( - decision_models - .iter() - .filter(|&m| { - !self.decision_models.iter().any(|x| { - x.partial_cmp(m) == Some(std::cmp::Ordering::Greater) - || x.partial_cmp(m) == Some(std::cmp::Ordering::Equal) - }) - }) - .map(|x| x.to_owned()), - ); - self.design_models_to_upload.extend( - design_models - .iter() - .filter(|&x| !self.design_models.contains(x)) - .map(|x| x.to_owned()), - ); - return self.next(); - } - - // fn collect_messages(&mut self) -> Vec<(String, String)> { - // self.messages - // .iter() - // .map(|msg| ("DEBUG".to_owned(), msg.to_owned())) - // .collect() - // } -} impl Drop for ExternalServerIdentifiticationIterator { fn drop(&mut self) { @@ -205,42 +155,17 @@ pub fn identification_procedure( let mut identified: Vec> = pre_identified.clone(); let mut messages: Vec<(String, String)> = Vec::new(); let mut fix_point = false; - // let mut iterators: Vec> = imodules - // .iter() - // .map(|imodule| imodule.identification_step(design_models, &identified)) - // .collect(); + let irules: Vec> = imodules + .iter() + .flat_map(|imodule| imodule.identification_rules().into_iter()) + .collect(); + debug!("Using {} identification rules", irules.len()); while !fix_point { fix_point = true; - // let before = identified.len(); - // let identified_step: Vec = (0..iterators.len()).into_par_iter().map(|i| { - // if let Ok(mut iter) = iterators[i].lock() { - // iter.next_with_models(&identified, design_models) - // } else { - // None - // } - // }).flatten().collect(); - - let (identified_models, msgs) = imodules + let (identified_models, msgs) = irules .par_iter() - .map(|imodule| imodule.identification_step(&identified, design_models)) - .reduce_with(|(models1, msgs1), (models2, msgs2)| { - let mut models = Vec::new(); - models.extend(models1.into_iter()); - for m in models2 { - if !models.contains(&m) { - models.push(m); - } - } - // models.extend(models2.into_iter().filter(|m| !models.contains(m))); - let mut msgs = Vec::new(); - msgs.extend(msgs1.into_iter()); - for msg in msgs2 { - if !msgs.contains(&msg) { - msgs.push(msg); - } - } - (models, msgs) - }) + .map(|irule| irule.identify(&design_models.as_slice(), identified.as_slice())) + .reduce_with(merge_identification_results) .unwrap_or((vec![], vec![])); // add completely new models or replace opaque deicion mdoels for non-opaque ones for m in &identified_models { @@ -267,28 +192,102 @@ pub fn identification_procedure( debug!("{}", msg); messages.push(("DEBUG".to_string(), msg.to_owned())); } - // let ident_messages: HashSet<(String, String)> = iterators - // .iter_mut() - // .flat_map(|iter| iter.collect_messages()) - // .collect(); - // for (lvl, msg) in ident_messages { - // } - // .filter(|potential| !identified.contains(potential)) - // .collect(); - // this contain check is done again because there might be imodules that identify the same decision model, - // and since the filtering before is step-based, it would add both identical decision models. - // This new for-if fixes this by checking every model of this step. debug!( "{} total decision models identified at step {}", identified.len(), step ); - // fix_point = fix_point && (identified.len() == before); step += 1; } (identified, messages) } +pub fn get_sqlite_for_identification(url: &str) -> Result { + let conn = rusqlite::Connection::open(url)?; + conn.execute( + "CREATE TABLE IF NOT EXISTS decision_models ( + id INTEGER PRIMARY KEY, + category TEXT NOT NULL, + body_cbor BLOB, + body_msgpack BLOB, + body_json JSON NOT NULL, + UNIQUE (category, body_json) + )", + [], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS design_models ( + id INTEGER PRIMARY KEY, + category TEXT NOT NULL, + format TEXT NOT NULL, + body TEXT NOT NULL, + UNIQUE (format, category, body) + )", + [], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS part ( + decision_model_id INTEGER NOT NULL, + element_name TEXT NOT NULL, + FOREIGN KEY (decision_model_id) REFERENCES decision_models (id), + UNIQUE (decision_model_id, element_name) + )", + [], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS elems ( + design_model_id INTEGER NOT NULL, + element_name TEXT NOT NULL, + FOREIGN KEY (design_model_id) REFERENCES decision_models (id), + UNIQUE (design_model_id, element_name) + )", + [], + )?; + Ok(conn) +} + +pub fn save_decision_model_sqlite( + url: &str, + decision_model: &T, +) -> Result { + let conn = get_sqlite_for_identification(url)?; + let id = conn.execute( + "INSERT INTO decision_models (category, body_cbor, body_msgpack, body_json) VALUES (?1, ?2, ?3, ?4)", params![ + decision_model.category(), + decision_model.body_as_cbor(), + decision_model.body_as_msgpack(), + decision_model.body_as_json() + ] + )?; + let mut stmt = + conn.prepare("INSERT INTO part (decision_model_id, element_name) VALUES (?1, ?2)")?; + for elem in decision_model.part() { + stmt.execute(params![id, elem])?; + } + Ok(id) +} + +pub fn save_design_model_sqlite( + url: &str, + design_model: &T, +) -> Result { + let conn = get_sqlite_for_identification(url)?; + let id = conn.execute( + "INSERT INTO design_models (category, format, body) VALUES (?1, ?2, ?3)", + params![ + design_model.category(), + design_model.format(), + design_model.body_as_string() + ], + )?; + let mut stmt = + conn.prepare("INSERT INTO elems (design_model_id, element_name) VALUES (?1, ?2)")?; + for elem in design_model.elements() { + stmt.execute(params![id, elem])?; + } + Ok(id) +} + // #[derive(Debug, PartialEq, Eq, Hash)] // pub struct ExternalIdentificationModule { // pub command_path_: PathBuf, diff --git a/rust-orchestration/src/lib.rs b/rust-orchestration/src/lib.rs index 6b970af9..bfe8190e 100644 --- a/rust-orchestration/src/lib.rs +++ b/rust-orchestration/src/lib.rs @@ -3,7 +3,6 @@ pub mod identification; use std::borrow::BorrowMut; use std::cmp::Ordering; -use std::collections::HashSet; use std::hash::Hash; use std::io::BufRead; @@ -20,12 +19,11 @@ use std::process::ChildStdout; use std::process::Stdio; use std::sync::Arc; use std::sync::Mutex; -use std::time::Duration; use exploration::ExternalExplorerBuilder; -use identification::ExternalServerIdentifiticationIterator; use idesyde_blueprints::IdentificationResultCompactMessage; +use idesyde_bridge_java::java_modules_from_jar_paths; use idesyde_core::DecisionModel; use idesyde_core::DesignModel; use idesyde_core::Explorer; @@ -38,9 +36,6 @@ use log::debug; use log::warn; use rayon::prelude::*; use reqwest::blocking::multipart::Form; -use reqwest::blocking::multipart::Part; -use serde::de; -use tungstenite::protocol::WebSocketConfig; use url::Url; use base64::{engine::general_purpose, Engine as _}; @@ -300,7 +295,8 @@ impl Module for ExternalServerModule { .for_each(|m| { if let Ok(bodyj) = m.to_json() { if let Ok(design_add_url) = self.url.join("/design/cache/add") { - if let Err(e) = self.client.put(design_add_url).body(bodyj).send() { + let form = Form::new().text("designModel", bodyj); + if let Err(e) = self.client.post(design_add_url).multipart(form).send() { debug!( "Failed to send design model to identify with: {}", e.to_string() @@ -340,7 +336,8 @@ impl Module for ExternalServerModule { // ); if let Ok(bodyj) = m.to_json() { if let Ok(decision_add_url) = self.url.join("/decision/cache/add") { - if let Err(e) = self.client.put(decision_add_url).body(bodyj).send() { + let form = Form::new().text("decisionModel", bodyj); + if let Err(e) = self.client.post(decision_add_url).multipart(form).send() { debug!( "Failed to send design model to identify with: {}", e.to_string() @@ -429,7 +426,9 @@ impl Module for ExternalServerModule { .for_each(|m| { if let Ok(bodyj) = m.to_json() { if let Ok(design_add_url) = self.url.join("/design/cache/add") { - if let Err(e) = self.client.put(design_add_url).body(bodyj).send() { + let form = Form::new().text("designModel", bodyj); + if let Err(e) = self.client.post(design_add_url).multipart(form).send() + { debug!( "Failed to send design model to reverse with: {}", e.to_string() @@ -459,7 +458,10 @@ impl Module for ExternalServerModule { .for_each(|m| { if let Ok(bodyj) = m.to_json() { if let Ok(decision_add_url) = self.url.join("/solved/cache/add") { - if let Err(e) = self.client.put(decision_add_url).body(bodyj).send() { + let form = Form::new().text("solvedModel", bodyj); + if let Err(e) = + self.client.post(decision_add_url).multipart(form).send() + { debug!( "Failed to send design model to reverse with: {}", e.to_string() @@ -679,39 +681,28 @@ impl Module for ExternalServerModule { } pub fn find_modules(modules_path: &Path) -> Vec> { - let mut imodules: Vec> = Vec::new(); + let mut modules: Vec> = Vec::new(); if let Ok(read_dir) = modules_path.read_dir() { - let prepared: Vec> = read_dir - .par_bridge() - .into_par_iter() - .flat_map(|e| { - if let Ok(de) = e { - let p = de.path(); - if p.is_file() { - let prog = p.read_link().unwrap_or(p); - if let Some(imodule) = ExternalServerModule::try_create_local(prog.clone()) - { - return Some(Arc::new(imodule) as Arc); - } - // else { - // return Some(Arc::new(ExternalIdentificationModule { - // command_path_: prog.clone(), - // identified_path_: identified_path.to_path_buf(), - // inputs_path_: inputs_path.to_path_buf(), - // solved_path_: solved_path.to_path_buf(), - // reverse_path_: integration_path.to_path_buf(), - // output_path_: output_path.to_path_buf(), - // }) - // as Arc); - // } - } - } - None + let jar_modules: Vec = read_dir + .filter_map(|e| e.ok()) + .map(|e| e.path()) + .filter(|p| p.is_file()) + .map(|p| p.read_link().unwrap_or(p)) + .filter(|p| { + p.extension() + .map(|ext| ext.eq_ignore_ascii_case("jar")) + .unwrap_or(false) }) .collect(); - imodules.extend(prepared.into_iter()); + let modules_result = java_modules_from_jar_paths(jar_modules.as_slice()); + for module in modules_result.result { + modules.push(Arc::new(module) as Arc); + } + for warn_msg in modules_result.warn { + warn!("{}", warn_msg); + } } - imodules + modules } // pub fn find_exploration_modules(modules_path: &Path) -> Vec> { diff --git a/rust-orchestration/src/main.rs b/rust-orchestration/src/main.rs index 334a36a8..16481418 100644 --- a/rust-orchestration/src/main.rs +++ b/rust-orchestration/src/main.rs @@ -3,12 +3,17 @@ use std::{cmp::Ordering, collections::HashSet, path::Path, sync::Arc}; use clap::Parser; use env_logger::WriteStyle; use idesyde_core::{ - explore_cooperatively, DecisionModel, DesignModel, ExplorationBid, ExplorationSolution, - Explorer, OpaqueDesignModel, + DecisionModel, DesignModel, ExplorationBid, ExplorationSolution, Explorer, OpaqueDesignModel, + ReverseIdentificationRuleLike, +}; +use idesyde_orchestration::{ + exploration::{self, explore_cooperatively}, + identification::identification_procedure, + ExternalServerModule, }; -use idesyde_orchestration::{identification::identification_procedure, ExternalServerModule, exploration}; use log::{debug, error, info, warn, Level}; use rayon::prelude::*; +use serde::de; #[derive(Parser, Debug)] #[command( @@ -59,14 +64,14 @@ struct Args { help = "Sets the desired maximum number of solutions. \nIf non-positive, there is no litmit", long_help = "Sets the desired maximum number of solutions. \nIf non-positive, there is no litmit. \nThe identification and integration stages are unnafected." )] - x_max_solutions: Option, + x_max_solutions: Option, #[arg( long, help = "Sets the desired maximum number of iterations after each exploration improvement. \nIf non-positive, there is no litmit", long_help = "Sets the desired maximum number of iterations after each exploration improvement. \nIf non-positive, there is no litmit. \nThe identification and integration stages are unnafected." )] - x_improvement_iterations: Option, + x_improvement_iterations: Option, #[arg( long, @@ -249,7 +254,7 @@ fn main() { let mut modules = idesyde_orchestration::find_modules(modules_path); // add embedded modules - modules.push(Arc::new(idesyde_common::make_common_module())); + modules.push(Arc::new(idesyde_common::make_module())); // add externally declared modules if let Some(external_modules) = args.module { @@ -345,7 +350,11 @@ fn main() { .unwrap_or("None".to_string()) ); for (i, m) in identified.iter().enumerate() { - m.write_to_dir(&identified_path, format!("final_{}", i).as_str(), "Orchestratror"); + m.write_to_dir( + &identified_path, + format!("final_{}", i).as_str(), + "Orchestratror", + ); } // println!( // "{}", @@ -365,13 +374,9 @@ fn main() { let biddings: Vec<(Arc, Arc, ExplorationBid)> = explorers .iter() .flat_map(|explorer| { - dominant_partial_identification.iter().map(|x| { - ( - explorer.clone(), - x.clone(), - explorer.bid(&explorers, x.clone()), - ) - }) + dominant_partial_identification + .iter() + .map(|x| (explorer.clone(), x.clone(), explorer.bid(x.clone()))) }) .filter(|(_, _, b)| b.can_explore) .filter(|(_, m, _)| { @@ -383,18 +388,41 @@ fn main() { bidding_time.elapsed().as_millis() ); let dominant_biddings_idx: Vec = idesyde_core::compute_dominant_biddings(&biddings); - info!("Acquired {} dominant bidding(s) out of {} bidding(s)", dominant_biddings_idx.len(), biddings.len()); + info!( + "Acquired {} dominant bidding(s) out of {} bidding(s)", + dominant_biddings_idx.len(), + biddings.len() + ); // let dominant_bidding_opt = // idesyde_core::compute_dominant_bidding(biddings.iter().map(|(_, _, b)| b)); - let total_identified_elements: HashSet = identified + let total_identifieable_elements: HashSet = design_models .iter() - .map(|x| x.part()) + .map(|x| x.elements()) .flatten() .collect(); - if !dominant_biddings_idx.iter().any(|i| biddings[*i].1.part() == total_identified_elements) { - warn!("No dominant bidding captures all partially identified elements. Double-check the final reversed models if any is produced."); - } if dominant_biddings_idx.len() > 0 { + if !dominant_biddings_idx.iter().any(|i| { + biddings[*i] + .1 + .part() + .is_superset(&total_identifieable_elements) + }) { + warn!("No dominant bidding captures all partially identified elements. Double-check any final reversed models if any is produced. You can see the non-identified elements by setting using DEBUG verbosity."); + debug!( + "Elements that are not covered are: {:?}", + total_identifieable_elements + .difference( + &dominant_biddings_idx + .iter() + .map(|i| biddings[*i].1.part()) + .flatten() + .collect() + ) + .map(|s| s.to_owned()) + .reduce(|s1, s2| format!("{}, {}", s1, s2)) + .unwrap_or("{}".to_string()) + ); + } match (args.x_total_time_out, args.x_max_solutions) { (Some(t), Some(n)) => info!( "Starting exploration up to {} total time-out seconds and {} solution(s)", @@ -421,32 +449,36 @@ fn main() { let mut dominant_sols: Vec = vec![]; let mut num_sols = 0; let exploration_time = std::time::Instant::now(); - for sol in explore_cooperatively( - &dominant_biddings_idx + let explorers_and_models: Vec<(Arc, Arc)> = + dominant_biddings_idx .iter() .map(|i| (biddings[*i].0.to_owned(), biddings[*i].1.to_owned())) - .collect(), - &dominant_biddings_idx - .iter() - .map(|i| biddings[*i].2.to_owned()) - .collect(), + .collect(); + let dominant_biddings: Vec = dominant_biddings_idx + .iter() + .map(|i| biddings[*i].2.to_owned()) + .collect(); + let conf = idesyde_core::ExplorationConfigurationBuilder::default() + .max_sols(args.x_max_solutions.unwrap_or(0)) + .total_timeout(args.x_total_time_out.unwrap_or(0)) + .time_resolution(args.x_time_resolution.unwrap_or(0)) + .memory_resolution(args.x_memory_resolution.unwrap_or(0)) + .strict(args.strict) + .improvement_timeout(args.x_improvement_time_out.unwrap_or(0)) + .improvement_iterations(args.x_improvement_iterations.unwrap_or(0)) + .target_objectives( + args.x_target_objectives + .iter() + .map(|x| x.to_string()) + .collect(), + ) + .build() + .expect("Failed to build explorer configuration. Should never fail."); + for sol in explore_cooperatively( + explorers_and_models.as_slice(), + dominant_biddings.as_slice(), &HashSet::new(), - idesyde_core::ExplorationConfigurationBuilder::default() - .max_sols(args.x_max_solutions.unwrap_or(0)) - .total_timeout(args.x_total_time_out.unwrap_or(0)) - .time_resolution(args.x_time_resolution.unwrap_or(0)) - .memory_resolution(args.x_memory_resolution.unwrap_or(0)) - .strict(args.strict) - .improvement_timeout(args.x_improvement_time_out.unwrap_or(0)) - .improvement_iterations(args.x_improvement_iterations.unwrap_or(0)) - .target_objectives( - args.x_target_objectives - .iter() - .map(|x| x.to_string()) - .collect(), - ) - .build() - .expect("Failed to build explorer configuration. Should never fail."), + &conf, ) { // let sol_dominated = dominant_sols.iter().any(|(_, y)| { // idesyde_core::pareto_dominance_partial_cmp(&sol.1, y) == Some(Ordering::Greater) @@ -528,23 +560,34 @@ fn main() { if !solved_models.is_empty() { info!("Starting reverse identification"); let reverse_time = std::time::Instant::now(); - let total_reversed: usize = modules + let all_reversed: usize = modules .par_iter() - .map(|imodule| { - let mut n_reversed = 0; - for reverse in - imodule.reverse_identification(&solved_models, &design_models) - { - // let reverse_header = reverse.header(); - reverse.write_to_dir( - &reverse_path, - format!("{}", n_reversed).as_str(), - imodule.unique_identifier().as_str(), - ); - n_reversed += 1; - debug!("Reverse identified a {} design model", reverse.category()); - } - n_reversed + .map(|module| { + module + .reverse_identification_rules() + .par_iter() + .map(|rrule| { + let (models, msgs) = + rrule.reverse_identify(&solved_models, &design_models); + for msg in msgs { + debug!("{}", msg); + } + let mut n_reversed = 0; + for model in &models { + model.write_to_dir( + &reverse_path, + format!("{}", n_reversed).as_str(), + module.unique_identifier().as_str(), + ); + n_reversed += 1; + debug!( + "Reverse identified a {} design model", + model.category() + ); + } + n_reversed + }) + .sum::() }) .sum(); debug!( @@ -553,7 +596,7 @@ fn main() { ); info!( "Finished reverse identification of {} design model(s)", - total_reversed + all_reversed ); } else { info!("No solution to reverse identify"); diff --git a/scala-blueprints/src/main/scala/idesyde/blueprints/StandaloneExplorationModule.scala b/scala-blueprints/src/main/scala/idesyde/blueprints/StandaloneExplorationModule.scala index 43fc1277..579b010d 100644 --- a/scala-blueprints/src/main/scala/idesyde/blueprints/StandaloneExplorationModule.scala +++ b/scala-blueprints/src/main/scala/idesyde/blueprints/StandaloneExplorationModule.scala @@ -474,6 +474,7 @@ trait StandaloneExplorationModule config.jetty.multipartConfig.maxTotalRequestSize(1, SizeUnit.GB); config.jetty.contextHandlerConfig(ctx => { ctx.setMaxFormContentSize(100000000); + }); config.jetty.wsFactoryConfig(wsconfig => { wsconfig.setMaxTextMessageSize(1000000000); diff --git a/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/identification/PlatformRules.scala b/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/identification/PlatformRules.scala index 19221a86..054f1ea0 100644 --- a/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/identification/PlatformRules.scala +++ b/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/identification/PlatformRules.scala @@ -2,12 +2,12 @@ package idesyde.devicetree.identification import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.common.SharedMemoryMultiCore +import idesyde.common.legacy.SharedMemoryMultiCore import idesyde.devicetree.utils.HasDeviceTreeUtils import scala.collection.mutable.Buffer import spire.math.Rational import scala.collection.mutable -import idesyde.common.PartitionedCoresWithRuntimes +import idesyde.common.legacy.PartitionedCoresWithRuntimes import idesyde.devicetree.RootNode trait PlatformRules extends HasDeviceTreeUtils { @@ -108,11 +108,11 @@ trait PlatformRules extends HasDeviceTreeUtils { PartitionedCoresWithRuntimes( processors = dm.description.oses.values.map(_.affinity.head).toVector, schedulers = dm.description.oses.keySet.toVector, - isBareMetal = + is_bare_metal = dm.description.oses.values.map(o => o.policy.exists(_ == "standalone")).toVector, - isFixedPriority = + is_fixed_priority = dm.description.oses.values.map(o => o.policy.exists(_.contains("FP"))).toVector, - isCyclicExecutive = + is_cyclic_executive = dm.description.oses.values.map(o => o.policy.exists(_.contains("SCS"))).toVector ) ), diff --git a/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/utils/HasDeviceTreeUtils.scala b/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/utils/HasDeviceTreeUtils.scala index 2796afdf..5175cfa1 100644 --- a/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/utils/HasDeviceTreeUtils.scala +++ b/scala-bridge-device-tree/src/main/scala/idesyde/devicetree/utils/HasDeviceTreeUtils.scala @@ -4,55 +4,64 @@ import idesyde.core.DecisionModel import idesyde.core.DesignModel import idesyde.devicetree.identification.DeviceTreeDesignModel import idesyde.devicetree.identification.OSDescriptionDesignModel +import idesyde.devicetree.identification.CanParseDeviceTree +import idesyde.core.OpaqueDesignModel +import idesyde.devicetree.OSDescription -trait HasDeviceTreeUtils { +import org.virtuslab.yaml.* + +trait HasDeviceTreeUtils extends CanParseDeviceTree { inline def toDeviceTreeDesignModel[M <: DecisionModel](models: Set[DesignModel])( inline body: (DeviceTreeDesignModel) => (Set[M], Set[String]) ): (Set[M], Set[String]) = { - val ms = models.flatMap(_ match { - case m: DeviceTreeDesignModel => Some(m) - case _ => None - }) - if (!ms.isEmpty) { - val mergedOpt = ms.tail.foldLeft(ms.headOption)((l, m) => - l.flatMap(lm => - lm.merge(m) - .flatMap(result => - result match { - case d: DeviceTreeDesignModel => Some(d) - case _ => None + var messages = scala.collection.mutable.Set[String]() + val allRoots = models + .flatMap(_ match { + case dt: DeviceTreeDesignModel => dt.roots + case m: OpaqueDesignModel => + if (m.format() == "dts") { + parseDeviceTree(m.body()) match { + case Success(result, next) => Some(result) + case Failure(msg, next) => { + messages += msg + None } - ) - ) - ) - mergedOpt.map(m => body(m)).getOrElse((Set(), Set())) - } else { - (Set(), Set()) - } + case Error(msg, next) => { + messages += msg + None + } + } + } else None + case _ => None + }) + val merged = DeviceTreeDesignModel(allRoots.toList) + val (ms, msgs) = body(merged) + (ms, msgs ++ messages.toSet) } inline def toOSDescriptionDesignModel[M <: DecisionModel](models: Set[DesignModel])( inline body: (OSDescriptionDesignModel) => (Set[M], Set[String]) ): (Set[M], Set[String]) = { - val ms = models.flatMap(_ match { - case m: OSDescriptionDesignModel => Some(m) - case _ => None - }) - if (!ms.isEmpty) { - val mergedOpt = ms.tail.foldLeft(ms.headOption)((l, m) => - l.flatMap(lm => - lm.merge(m) - .flatMap(result => - result match { - case d: OSDescriptionDesignModel => Some(d) - case _ => None + var messages = scala.collection.mutable.Set[String]() + val allOSes = models + .flatMap(designModel => designModel match { + case osDesc: OSDescriptionDesignModel => Some(osDesc.description) + case m: OpaqueDesignModel => + if (m.format() == "yaml") { + m.body().as[OSDescription] match { + case Left(value) => { + messages += "Failed to parse OSDescriptionDesignModel: " + value.msg + None } - ) - ) - ) - mergedOpt.map(m => body(m)).getOrElse((Set(), Set())) - } else { - (Set(), Set()) + case Right(value) => Some(value) + } + } else None + case _ => None + }) + val merged = allOSes.reduceOption((a, b) => a.mergeLeft(b)) + merged.map(desc => body(OSDescriptionDesignModel(desc))) match { + case None => (Set(), messages.toSet) + case Some((ms, msgs)) => (ms, msgs ++ messages.toSet) } } diff --git a/scala-bridge-forsyde-io/src/main/resources/META-INF/idesyde/automodules b/scala-bridge-forsyde-io/src/main/resources/META-INF/idesyde/automodules new file mode 100644 index 00000000..1572fc1d --- /dev/null +++ b/scala-bridge-forsyde-io/src/main/resources/META-INF/idesyde/automodules @@ -0,0 +1 @@ +idesyde.forsydeio.legacy.ForSyDeIOScalaModule \ No newline at end of file diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeIdentificationUtils.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeIdentificationUtils.scala deleted file mode 100644 index ebd62567..00000000 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeIdentificationUtils.scala +++ /dev/null @@ -1,21 +0,0 @@ -package idesyde.forsydeio - -import idesyde.core.DesignModel -import idesyde.core.DecisionModel -import idesyde.forsydeio.ForSyDeDesignModel -import forsyde.io.core.SystemGraph - -object ForSyDeIdentificationUtils { - - inline def toForSyDe[M <: DecisionModel](models: Set[DesignModel])( - inline body: (SystemGraph) => (Set[M], Set[String]) - ): (Set[M], Set[String]) = { - models - .filter(_.isInstanceOf[ForSyDeDesignModel]) - .map(_.asInstanceOf[ForSyDeDesignModel]) - .map(_.systemGraph) - .reduceOption(_.merge(_)) - .map(body(_)) - .getOrElse((Set(), Set("No ForSyDe model present"))) - } -} diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ApplicationRules.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ApplicationRules.scala similarity index 94% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ApplicationRules.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ApplicationRules.scala index 666037d3..2a877487 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ApplicationRules.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ApplicationRules.scala @@ -1,12 +1,12 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import scala.jdk.CollectionConverters._ import scala.jdk.OptionConverters._ -import idesyde.forsydeio.ForSyDeIdentificationUtils +import idesyde.forsydeio.legacy.ForSyDeIdentificationUtils import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.common.AperiodicAsynchronousDataflow +import idesyde.common.legacy.AperiodicAsynchronousDataflow import scala.collection.mutable import forsyde.io.lib.hierarchy.ForSyDeHierarchy import forsyde.io.lib.hierarchy.behavior.moc.sy.SYMap diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeDesignModel.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeDesignModel.scala similarity index 75% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeDesignModel.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeDesignModel.scala index cfeaf348..398f0e35 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeDesignModel.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeDesignModel.scala @@ -1,4 +1,4 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import scala.jdk.CollectionConverters.* import scala.jdk.OptionConverters.* @@ -8,9 +8,8 @@ import forsyde.io.core.SystemGraph import forsyde.io.core.EdgeInfo import forsyde.io.core.Vertex import forsyde.io.core.ModelHandler -import forsyde.io.lib.TraitNamesFrom0_6To0_7 -import idesyde.forsydeio.ForSyDeDesignModel.modelHandler import forsyde.io.lib.LibForSyDeModelHandler +import forsyde.io.bridge.sdf3.drivers.SDF3Driver final case class ForSyDeDesignModel(val systemGraph: SystemGraph) extends DesignModel { @@ -37,23 +36,27 @@ final case class ForSyDeDesignModel(val systemGraph: SystemGraph) extends Design // rel.getTargetPort().toScala // ) - override def elements() = (systemGraph.vertexSet().asScala.map(_.getIdentifier()) ++ systemGraph.edgeSet().asScala.map(_.toIDString())).asJava + override def elements() = (systemGraph + .vertexSet() + .asScala + .map(_.getIdentifier()) + .asJava) // ++ systemGraph.edgeSet().asScala.map(_.toIDString())).asJava override def category(): String = "ForSyDeDesignModel" override def format() = "fiodl" override def asString(): java.util.Optional[String] = { - java.util.Optional.of(modelHandler.printModel(systemGraph, "fiodl")) + java.util.Optional.of(ForSyDeDesignModel.modelHandler.printModel(systemGraph, "fiodl")) } def bodyAsText: Option[String] = { - Some(modelHandler.printModel(systemGraph, "fiodl")) + Some(ForSyDeDesignModel.modelHandler.printModel(systemGraph, "fiodl")) } } object ForSyDeDesignModel { - val modelHandler = LibForSyDeModelHandler.registerLibForSyDe(ModelHandler()) + val modelHandler = LibForSyDeModelHandler.registerLibForSyDe(ModelHandler()).registerDriver(new SDF3Driver()) def fromText(s: String): Option[ForSyDeDesignModel] = { try { diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeIOScalaModule.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeIOScalaModule.scala similarity index 80% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeIOScalaModule.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeIOScalaModule.scala index 5e1fd024..343a9d5e 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/ForSyDeIOScalaModule.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeIOScalaModule.scala @@ -1,4 +1,4 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import scala.jdk.CollectionConverters._ import scala.jdk.OptionConverters._ @@ -10,53 +10,53 @@ import java.{util => ju} import idesyde.core.IdentificationRule import idesyde.core.IdentificationResult import idesyde.core.ReverseIdentificationRule -import idesyde.forsydeio.MixedRules -import idesyde.forsydeio.SDFRules -import idesyde.forsydeio.PlatformRules -import idesyde.forsydeio.WorkloadRules +import idesyde.forsydeio.legacy.MixedRules +import idesyde.forsydeio.legacy.SDFRules +import idesyde.forsydeio.legacy.PlatformRules +import idesyde.forsydeio.legacy.WorkloadRules import idesyde.core.DecisionModel import idesyde.core.DesignModel +import idesyde.core.Module import forsyde.io.core.ModelHandler -import idesyde.forsydeio.ForSyDeDesignModel +import idesyde.forsydeio.legacy.ForSyDeDesignModel import java.nio.file.Paths -import idesyde.common.SDFToTiledMultiCore -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore import java.nio.file.Files import forsyde.io.bridge.sdf3.drivers.SDF3Driver import forsyde.io.lib.hierarchy.ForSyDeHierarchy import forsyde.io.lib.LibForSyDeModelHandler import java.io.StringReader -import idesyde.common.AperiodicAsynchronousDataflow +import idesyde.common.legacy.AperiodicAsynchronousDataflow import idesyde.core.OpaqueDesignModel import idesyde.core.OpaqueDecisionModel import idesyde.blueprints.StandaloneModule -import idesyde.common.SDFApplication -import idesyde.common.AnalysedSDFApplication -import idesyde.common.TiledMultiCoreWithFunctions -import idesyde.common.PartitionedCoresWithRuntimes -import idesyde.common.SchedulableTiledMultiCore -import idesyde.common.SharedMemoryMultiCore -import idesyde.common.CommunicatingAndTriggeredReactiveWorkload -import idesyde.common.PartitionedSharedMemoryMultiCore -import idesyde.common.PeriodicWorkloadAndSDFServers +import idesyde.common.legacy.SDFApplication +import idesyde.common.legacy.AnalysedSDFApplication +import idesyde.common.legacy.TiledMultiCoreWithFunctions +import idesyde.common.legacy.PartitionedCoresWithRuntimes +import idesyde.common.legacy.SchedulableTiledMultiCore +import idesyde.common.legacy.SharedMemoryMultiCore +import idesyde.common.legacy.CommunicatingAndTriggeredReactiveWorkload +import idesyde.common.legacy.PartitionedSharedMemoryMultiCore +import idesyde.common.legacy.PeriodicWorkloadAndSDFServers import idesyde.devicetree.OSDescription import idesyde.devicetree.identification.OSDescriptionDesignModel import idesyde.devicetree.identification.CanParseDeviceTree import idesyde.devicetree.identification.DeviceTreeDesignModel import idesyde.choco.ChocoExplorer -import idesyde.common.PeriodicWorkloadAndSDFServerToMultiCoreOld +import idesyde.common.legacy.PeriodicWorkloadAndSDFServerToMultiCoreOld -object ForSyDeIOScalaModule - extends StandaloneModule - with MixedRules +class ForSyDeIOScalaModule + extends Module + with idesyde.forsydeio.legacy.MixedRules with SDFRules - with PlatformRules - with WorkloadRules - with ApplicationRules - with idesyde.common.MixedRules - with idesyde.common.PlatformRules - with idesyde.common.WorkloadRules - with idesyde.common.ApplicationRules + with idesyde.forsydeio.legacy.PlatformRules + with idesyde.forsydeio.legacy.WorkloadRules + with idesyde.common.legacy.MixedRules + with idesyde.common.legacy.PlatformRules + with idesyde.common.legacy.WorkloadRules + with idesyde.common.legacy.ApplicationRules with idesyde.devicetree.identification.PlatformRules with CanParseDeviceTree { @@ -79,7 +79,7 @@ object ForSyDeIOScalaModule func(a.asScala.toSet, b.asScala.toSet).map(_.asInstanceOf[DesignModel]).asJava } - override def fromOpaqueDecision(opaque: OpaqueDecisionModel): ju.Optional[DecisionModel] = { + def fromOpaqueDecision(opaque: OpaqueDecisionModel): ju.Optional[DecisionModel] = { opaque.category() match { case "SDFToTiledMultiCore" => opaque @@ -181,23 +181,25 @@ object ForSyDeIOScalaModule ), IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identSDFToPartitionedSharedMemory)), IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identSDFToTiledMultiCore)), - // IdentificationRule.OnlyCertainDecisionModels( - // adaptIRuleToJava(identAnalysedSDFApplication), - // Set("SDFApplication", "SDFApplicationWithFunctions").asJava - // ), + IdentificationRule.OnlyCertainDecisionModels( + adaptIRuleToJava(identAnalysedSDFApplication), + Set("SDFApplication", "SDFApplicationWithFunctions").asJava + ), IdentificationRule.OnlyDecisionModels( adaptIRuleToJava(identPeriodicWorkloadToPartitionedSharedMultiCore) ), IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identTaksAndSDFServerToMultiCore)), IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identTiledFromShared)), IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identTaskdAndSDFServer)), - // IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identCommonSDFApplication)), + IdentificationRule.OnlyDecisionModels(adaptIRuleToJava(identCommonSDFApplication)), IdentificationRule.OnlyCertainDecisionModels( adaptIRuleToJava(identAggregatedCommunicatingAndTriggeredReactiveWorkload), Set("CommunicatingAndTriggeredReactiveWorkload").asJava ) ).asJava + def identificationRulesCanonicalClassesNames(): Array[String] = identificationRules().asScala.map(cls => cls.getClass().getCanonicalName()).toArray + override def reverseIdentificationRules(): ju.Set[ReverseIdentificationRule] = Set( ReverseIdentificationRule.Generic( adaptRevRuleToJava(integratePeriodicWorkloadToPartitionedSharedMultiCore) @@ -210,10 +212,10 @@ object ForSyDeIOScalaModule override def explorers() = Set(ChocoExplorer()).asJava - def main(args: Array[String]): Unit = - standaloneModule(args).ifPresent(javalin => javalin.start(0)) + // def main(args: Array[String]): Unit = + // standaloneModule(args).ifPresent(javalin => javalin.start(0)) - override def fromOpaqueDesign(opaque: OpaqueDesignModel): ju.Optional[DesignModel] = { + def fromOpaqueDesign(opaque: OpaqueDesignModel): ju.Optional[DesignModel] = { if (modelHandler.canLoadModel(opaque.format())) { return opaque .asString() diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeIdentificationUtils.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeIdentificationUtils.scala new file mode 100644 index 00000000..fa5e783c --- /dev/null +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/ForSyDeIdentificationUtils.scala @@ -0,0 +1,42 @@ +package idesyde.forsydeio.legacy + +import scala.jdk.OptionConverters._ +import scala.jdk.CollectionConverters._ + +import idesyde.core.DesignModel +import idesyde.core.DecisionModel +import idesyde.forsydeio.legacy.ForSyDeDesignModel +import forsyde.io.core.SystemGraph +import idesyde.core.OpaqueDesignModel +import idesyde.forsydeio.legacy.ForSyDeDesignModel.modelHandler +import idesyde.core.OpaqueDecisionModel + +object ForSyDeIdentificationUtils { + + inline def toForSyDe[M <: DecisionModel](models: Set[DesignModel])( + inline body: (SystemGraph) => (Set[M], Set[String]) + ): (Set[M], Set[String]) = { + var messages = scala.collection.mutable.Set[String]() + models + .flatMap(_ match { + case ForSyDeDesignModel(systemGraph) => Some(systemGraph) + case m: OpaqueDesignModel => + if (modelHandler.canLoadModel(m.format())) { + try { + Some(modelHandler.readModel(m.body(), m.format())) + } catch { + case e: Exception => { + messages += e.getMessage() + None + } + } + } else None + case _ => None + }) + .reduceOption(_.merge(_)) + .map(body(_)) + .map((a, b) => (a, b ++ messages.toSet)) + .getOrElse((Set(), messages.toSet ++ Set("No ForSyDe IO compliant model present"))) + } + +} diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/MixedRules.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/MixedRules.scala similarity index 57% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/MixedRules.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/MixedRules.scala index 6e457519..e806b927 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/MixedRules.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/MixedRules.scala @@ -1,14 +1,14 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.forsydeio.ForSyDeDesignModel -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore -import idesyde.common.SDFToTiledMultiCore +import idesyde.forsydeio.legacy.ForSyDeDesignModel +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore import forsyde.io.core.SystemGraph -import idesyde.common.CommunicatingAndTriggeredReactiveWorkload -import idesyde.common.PartitionedSharedMemoryMultiCore -import idesyde.forsydeio.ForSyDeIdentificationUtils +import idesyde.common.legacy.CommunicatingAndTriggeredReactiveWorkload +import idesyde.common.legacy.PartitionedSharedMemoryMultiCore +import idesyde.forsydeio.legacy.ForSyDeIdentificationUtils import spire.math.Rational import scala.jdk.CollectionConverters._ import scala.collection.mutable.Buffer @@ -16,9 +16,10 @@ import forsyde.io.lib.hierarchy.platform.hardware.GenericMemoryModule import forsyde.io.lib.hierarchy.platform.runtime.AbstractRuntime import forsyde.io.lib.hierarchy.ForSyDeHierarchy import forsyde.io.lib.hierarchy.platform.runtime.SuperLoopRuntime -import idesyde.common.InstrumentedComputationTimes +import idesyde.common.legacy.InstrumentedComputationTimes import scala.collection.mutable -import idesyde.common.PeriodicWorkloadAndSDFServerToMultiCoreOld +import idesyde.common.legacy.PeriodicWorkloadAndSDFServerToMultiCoreOld +import idesyde.common.legacy.CommonModule.tryCast trait MixedRules { @@ -115,61 +116,55 @@ trait MixedRules { decisionModel: Set[DecisionModel], designModel: Set[DesignModel] ): Set[ForSyDeDesignModel] = { - // .flatMap(_ match { - // case ForSyDeDesignModel(forSyDeSystemGraph) => - // Some(forSyDeSystemGraph) - // case _ => None - // }) - // .foldRight(SystemGraph())((a, b) => b.merge(a)) - val solveds = decisionModel.flatMap(_ match { - case dse: PeriodicWorkloadToPartitionedSharedMultiCore => { - if ( - !dse.processMappings.isEmpty && !dse.processSchedulings.isEmpty && !dse.channelMappings.isEmpty - ) - Some(dse) - else None - } - case _ => None - }) - for (solved <- solveds; rebuilt = SystemGraph()) yield { - for ( - (taskId, schedId) <- solved.processSchedulings; - // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) - // TODO: fix it to be stable later - task = ForSyDeHierarchy.Scheduled - .enforce(rebuilt, rebuilt.newVertex(taskId)); - sched = ForSyDeHierarchy.AbstractRuntime - .enforce(rebuilt, rebuilt.newVertex(schedId)) - ) { - task.runtimeHost(sched) - ForSyDeHierarchy.GreyBox - .enforce(sched) - .addContained(ForSyDeHierarchy.Visualizable.enforce(task)) - } - for ( - (taskId, memId) <- solved.processMappings; - // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) - // TODO: fix it to be stable later - task = ForSyDeHierarchy.MemoryMapped - .enforce(rebuilt, rebuilt.newVertex(taskId)); - mem = ForSyDeHierarchy.GenericMemoryModule - .enforce(rebuilt, rebuilt.newVertex(memId)) - ) { - task.mappingHost(mem) - } - for ( - (channelId, memId) <- solved.channelMappings; - // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) - // TODO: fix it to be stable later - channel = ForSyDeHierarchy.MemoryMapped - .enforce(rebuilt, rebuilt.newVertex(channelId)); - mem = ForSyDeHierarchy.GenericMemoryModule - .enforce(rebuilt, rebuilt.newVertex(memId)) - ) { - channel.mappingHost(mem) - ForSyDeHierarchy.GreyBox.enforce(mem).addContained(ForSyDeHierarchy.Visualizable.enforce(channel)) + // .flatMap(_ match { + // case ForSyDeDesignModel(forSyDeSystemGraph) => + // Some(forSyDeSystemGraph) + // case _ => None + // }) + // .foldRight(SystemGraph())((a, b) => b.merge(a)) + tryCast(decisionModel, classOf[PeriodicWorkloadToPartitionedSharedMultiCore]) { solveds => + for (solved <- solveds; rebuilt = SystemGraph()) yield { + for ( + (taskId, schedId) <- solved.processSchedulings; + // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) + // TODO: fix it to be stable later + task = ForSyDeHierarchy.Scheduled + .enforce(rebuilt, rebuilt.newVertex(taskId)); + sched = ForSyDeHierarchy.AbstractRuntime + .enforce(rebuilt, rebuilt.newVertex(schedId)) + ) { + task.runtimeHost(sched) + ForSyDeHierarchy.GreyBox + .enforce(sched) + .addContained(ForSyDeHierarchy.Visualizable.enforce(task)) + } + for ( + (taskId, memId) <- solved.processMappings; + // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) + // TODO: fix it to be stable later + task = ForSyDeHierarchy.MemoryMapped + .enforce(rebuilt, rebuilt.newVertex(taskId)); + mem = ForSyDeHierarchy.GenericMemoryModule + .enforce(rebuilt, rebuilt.newVertex(memId)) + ) { + task.mappingHost(mem) + } + for ( + (channelId, memId) <- solved.channelMappings; + // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) + // TODO: fix it to be stable later + channel = ForSyDeHierarchy.MemoryMapped + .enforce(rebuilt, rebuilt.newVertex(channelId)); + mem = ForSyDeHierarchy.GenericMemoryModule + .enforce(rebuilt, rebuilt.newVertex(memId)) + ) { + channel.mappingHost(mem) + ForSyDeHierarchy.GreyBox + .enforce(mem) + .addContained(ForSyDeHierarchy.Visualizable.enforce(channel)) + } + ForSyDeDesignModel(rebuilt) } - ForSyDeDesignModel(rebuilt) } } @@ -177,25 +172,8 @@ trait MixedRules { decisionModel: Set[DecisionModel], designModel: Set[DesignModel] ): Set[ForSyDeDesignModel] = { - val model = designModel - .flatMap(_ match { - case ForSyDeDesignModel(forSyDeSystemGraph) => - Some(forSyDeSystemGraph) - case _ => None - }) - .foldRight(SystemGraph())((a, b) => b.merge(a)) - val solveds = decisionModel.flatMap(_ match { - case dse: PeriodicWorkloadToPartitionedSharedMultiCore => { - if ( - !dse.processMappings.isEmpty && !dse.processSchedulings.isEmpty && !dse.channelMappings.isEmpty - ) - Some(dse) - else None - } - case _ => None - }) - if (model.vertexSet().isEmpty()) { - for (solved <- solveds; rebuilt = SystemGraph().merge(model)) yield { + tryCast(decisionModel, classOf[PeriodicWorkloadToPartitionedSharedMultiCore]) { solveds => + for (solved <- solveds; rebuilt = SystemGraph()) yield { for ( (taskId, schedId) <- solved.processSchedulings; // ok for now because it is a 1-to-many situation wit the current Decision Models (2023-01-16) @@ -234,7 +212,7 @@ trait MixedRules { } ForSyDeDesignModel(rebuilt) } - } else Set() + } } def integratePeriodicWorkloadAndSDFServerToMultiCoreOld( @@ -268,10 +246,13 @@ trait MixedRules { .addContained(ForSyDeHierarchy.Visualizable.enforce(task)) val taskIdx = solved.tasksAndSDFs.workload.tasks.indexOf(taskId) if (taskIdx > -1) { - ForSyDeHierarchy.FixedPriorityScheduledRuntime.enforce(sched).priorityAssignments().put( - taskId, - priorities(taskIdx) - ) + ForSyDeHierarchy.FixedPriorityScheduledRuntime + .enforce(sched) + .priorityAssignments() + .put( + taskId, + priorities(taskIdx) + ) } } for ( @@ -298,7 +279,7 @@ trait MixedRules { ForSyDeHierarchy.GreyBox .enforce(mem) .addContained(ForSyDeHierarchy.Visualizable.enforce(channel)) - } + } // now, we put the schedule in each scheduler for ( (list, si) <- solved.sdfOrderBasedSchedules.zipWithIndex; @@ -362,121 +343,116 @@ trait MixedRules { decisionModel: Set[DecisionModel], designModel: Set[DesignModel] ): Set[ForSyDeDesignModel] = { - val solveds = decisionModel.flatMap(_ match { - case dse: SDFToTiledMultiCore => { - if (!dse.messageMappings.isEmpty && !dse.processMappings.isEmpty) - Some(dse) - else None - } - case _ => None - }) - for (solved <- solveds; rebuilt = SystemGraph()) yield { - // first, we take care of the process mappings - for ( - (mem, i) <- solved.processMappings.zipWithIndex; - actorId = solved.sdfApplications.actorsIdentifiers(i); - memIdx = solved.platform.hardware.memories.indexOf(mem); - proc = solved.platform.hardware.processors(memIdx); - scheduler = solved.platform.runtimes.schedulers(memIdx) - ) { - val v = - ForSyDeHierarchy.MemoryMapped.enforce( + tryCast(decisionModel, classOf[SDFToTiledMultiCore]) { filtered => + val solveds = filtered.filter(m => !m.processMappings.isEmpty && !m.messageMappings.isEmpty) + for (solved <- solveds; rebuilt = SystemGraph()) yield { + // first, we take care of the process mappings + for ( + (mem, i) <- solved.processMappings.zipWithIndex; + actorId = solved.sdfApplications.actorsIdentifiers(i); + memIdx = solved.platform.hardware.memories.indexOf(mem); + proc = solved.platform.hardware.processors(memIdx); + scheduler = solved.platform.runtimes.schedulers(memIdx) + ) { + val v = + ForSyDeHierarchy.MemoryMapped.enforce( + rebuilt, + rebuilt.newVertex(actorId) + ) + val m = + ForSyDeHierarchy.GenericMemoryModule.enforce( + rebuilt, + rebuilt.newVertex(mem) + ) + v.mappingHost( + m + ) + val s = ForSyDeHierarchy.AbstractRuntime.enforce( rebuilt, - rebuilt.newVertex(actorId) + rebuilt.newVertex(scheduler) ) - val m = - ForSyDeHierarchy.GenericMemoryModule.enforce( + ForSyDeHierarchy.Scheduled + .enforce(v) + .runtimeHost(s) + ForSyDeHierarchy.GreyBox.enforce(s).addContained(ForSyDeHierarchy.Visualizable.enforce(v)) + } + // now, we take care of the memory mappings + for ( + (mem, i) <- solved.messageMappings.zipWithIndex; + channelID = solved.sdfApplications.channelsIdentifiers(i); + memIdx = solved.platform.hardware.memories.indexOf(mem) + ) { + val v = + ForSyDeHierarchy.MemoryMapped.enforce( + rebuilt, + rebuilt.newVertex(channelID) + ) + val m = + ForSyDeHierarchy.GenericMemoryModule.enforce( + rebuilt, + rebuilt.newVertex(mem) + ) + v.mappingHost(m) + ForSyDeHierarchy.GreyBox.enforce(m).addContained(ForSyDeHierarchy.Visualizable.enforce(v)) + } + // now, we put the schedule in each scheduler + for ( + (list, si) <- solved.schedulerSchedules.zipWithIndex; + proc = solved.platform.hardware.processors(si); + scheduler = solved.platform.runtimes.schedulers(si) + ) { + val scs = ForSyDeHierarchy.SuperLoopRuntime.enforce( rebuilt, - rebuilt.newVertex(mem) + rebuilt.newVertex(scheduler) ) - v.mappingHost( - m - ) - val s = ForSyDeHierarchy.AbstractRuntime.enforce( - rebuilt, - rebuilt.newVertex(scheduler) + scs.superLoopEntries(list.asJava) + } + // finally, the channel comm allocations + var commAllocs = solved.platform.hardware.communicationElementsMaxChannels.map(maxVc => + Buffer.fill(maxVc)(Buffer.empty[String]) ) - ForSyDeHierarchy.Scheduled - .enforce(v) - .runtimeHost(s) - ForSyDeHierarchy.GreyBox.enforce(s).addContained(ForSyDeHierarchy.Visualizable.enforce(v)) - } - // now, we take care of the memory mappings - for ( - (mem, i) <- solved.messageMappings.zipWithIndex; - channelID = solved.sdfApplications.channelsIdentifiers(i); - memIdx = solved.platform.hardware.memories.indexOf(mem) - ) { - val v = - ForSyDeHierarchy.MemoryMapped.enforce( + for ( + (maxVc, ce) <- solved.platform.hardware.communicationElementsMaxChannels.zipWithIndex; + (dict, c) <- solved.messageSlotAllocations.zipWithIndex; + vc <- 0 until maxVc; + commElem = solved.platform.hardware.communicationElems(ce); + if dict.getOrElse(commElem, Vector.fill(maxVc)(false))(vc); + cId = solved.sdfApplications.channelsIdentifiers(c) + ) { + commAllocs(ce)(vc) += cId + } + for ((ce, i) <- solved.platform.hardware.communicationElems.zipWithIndex) { + val comm = ForSyDeHierarchy.ConcurrentSlotsReserved.enforce( rebuilt, - rebuilt.newVertex(channelID) + rebuilt.newVertex(ce) ) - val m = - ForSyDeHierarchy.GenericMemoryModule.enforce( + comm.slotReservations(commAllocs(i).map(_.asJava).asJava) + } + // add the throughputs for good measure + for ( + (a, ai) <- solved.sdfApplications.actorsIdentifiers.zipWithIndex; + th = solved.sdfApplications.minimumActorThroughputs(ai) + ) { + val act = ForSyDeHierarchy.AnalyzedActor.enforce( rebuilt, - rebuilt.newVertex(mem) + rebuilt.newVertex(a) ) - v.mappingHost(m) - ForSyDeHierarchy.GreyBox.enforce(m).addContained(ForSyDeHierarchy.Visualizable.enforce(v)) - } - // now, we put the schedule in each scheduler - for ( - (list, si) <- solved.schedulerSchedules.zipWithIndex; - proc = solved.platform.hardware.processors(si); - scheduler = solved.platform.runtimes.schedulers(si) - ) { - val scs = ForSyDeHierarchy.SuperLoopRuntime.enforce( - rebuilt, - rebuilt.newVertex(scheduler) - ) - scs.superLoopEntries(list.asJava) - } - // finally, the channel comm allocations - var commAllocs = solved.platform.hardware.communicationElementsMaxChannels.map(maxVc => - Buffer.fill(maxVc)(Buffer.empty[String]) - ) - for ( - (maxVc, ce) <- solved.platform.hardware.communicationElementsMaxChannels.zipWithIndex; - (dict, c) <- solved.messageSlotAllocations.zipWithIndex; - vc <- 0 until maxVc; - commElem = solved.platform.hardware.communicationElems(ce); - if dict.getOrElse(commElem, Vector.fill(maxVc)(false))(vc); - cId = solved.sdfApplications.channelsIdentifiers(c) - ) { - commAllocs(ce)(vc) += cId - } - for ((ce, i) <- solved.platform.hardware.communicationElems.zipWithIndex) { - val comm = ForSyDeHierarchy.ConcurrentSlotsReserved.enforce( - rebuilt, - rebuilt.newVertex(ce) - ) - comm.slotReservations(commAllocs(i).map(_.asJava).asJava) - } - // add the throughputs for good measure - for ( - (a, ai) <- solved.sdfApplications.actorsIdentifiers.zipWithIndex; - th = solved.sdfApplications.minimumActorThroughputs(ai) - ) { - val act = ForSyDeHierarchy.AnalyzedActor.enforce( - rebuilt, - rebuilt.newVertex(a) - ) - val frac = Rational(th) - act.setThroughputInSecsNumerator(frac.numeratorAsLong) - act.setThroughputInSecsDenominator(frac.denominatorAsLong) - } - // and the maximum channel sizes - for ( - (c, ci) <- solved.sdfApplications.channelsIdentifiers.zipWithIndex; - maxTokens = solved.sdfApplications.sdfPessimisticTokensPerChannel(ci) - ) { - val channelVec = rebuilt.newVertex(c) - val bounded = ForSyDeHierarchy.BoundedBufferLike.enforce(rebuilt, channelVec) - bounded.elementSizeInBits(solved.sdfApplications.channelTokenSizes(ci)) - bounded.maxElements(maxTokens) + val frac = Rational(th) + act.setThroughputInSecsNumerator(frac.numeratorAsLong) + act.setThroughputInSecsDenominator(frac.denominatorAsLong) + } + // and the maximum channel sizes + for ( + (c, ci) <- solved.sdfApplications.channelsIdentifiers.zipWithIndex; + maxTokens = solved.sdfApplications.sdfPessimisticTokensPerChannel(ci) + ) { + val channelVec = rebuilt.newVertex(c) + val bounded = ForSyDeHierarchy.BoundedBufferLike.enforce(rebuilt, channelVec) + bounded.elementSizeInBits(solved.sdfApplications.channelTokenSizes(ci)) + bounded.maxElements(maxTokens) + } + ForSyDeDesignModel(rebuilt) } - ForSyDeDesignModel(rebuilt) } } @@ -485,25 +461,12 @@ trait MixedRules { identified: Set[DecisionModel] ): (Set[PeriodicWorkloadToPartitionedSharedMultiCore], Set[String]) = { ForSyDeIdentificationUtils.toForSyDe(models) { model => - val app = identified - .filter(_.isInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) - .map(_.asInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) - val plat = identified - .filter(_.isInstanceOf[PartitionedSharedMemoryMultiCore]) - .map(_.asInstanceOf[PartitionedSharedMemoryMultiCore]) - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - ( - app.flatMap(a => - plat.map(p => - PeriodicWorkloadToPartitionedSharedMultiCore( - workload = a, - platform = p, - processMappings = Vector.empty, - processSchedulings = Vector.empty, - channelMappings = Vector.empty, - channelSlotAllocations = Map(), - maxUtilizations = (for ( - pe <- p.hardware.processingElems; + tryCast(identified, classOf[PeriodicWorkloadToPartitionedSharedMultiCore]) { fulls => + ( + fulls.map( + _.copy(maxUtilizations = + (for ( + pe <- fulls.head.platform.hardware.processingElems; peVertex = model.queryVertex(pe); if peVertex.isPresent() && ForSyDeHierarchy.UtilizationBound .tryView(model, peVertex.get()) @@ -512,10 +475,17 @@ trait MixedRules { ) yield pe -> utilVertex.maxUtilization().toDouble).toMap ) - ) - ), - Set() - ) + ), + Set() + ) + } + // val app = identified + // .filter(_.isInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) + // .map(_.asInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) + // val plat = identified + // .filter(_.isInstanceOf[PartitionedSharedMemoryMultiCore]) + // .map(_.asInstanceOf[PartitionedSharedMemoryMultiCore]) + // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) } } } diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/PlatformRules.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/PlatformRules.scala similarity index 95% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/PlatformRules.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/PlatformRules.scala index 6d59700b..1461a4bd 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/PlatformRules.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/PlatformRules.scala @@ -1,17 +1,17 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import scala.jdk.CollectionConverters._ import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.common.TiledMultiCoreWithFunctions -import idesyde.forsydeio.ForSyDeDesignModel +import idesyde.common.legacy.TiledMultiCoreWithFunctions +import idesyde.forsydeio.legacy.ForSyDeDesignModel import scala.collection.mutable.Buffer import scala.collection.mutable import spire.math.Rational -import idesyde.common.PartitionedCoresWithRuntimes -import idesyde.common.SharedMemoryMultiCore -import idesyde.forsydeio.ForSyDeIdentificationUtils +import idesyde.common.legacy.PartitionedCoresWithRuntimes +import idesyde.common.legacy.SharedMemoryMultiCore +import idesyde.forsydeio.legacy.ForSyDeIdentificationUtils import org.jgrapht.graph.AsSubgraph import org.jgrapht.alg.connectivity.ConnectivityInspector import forsyde.io.lib.hierarchy.platform.hardware.GenericProcessingModule @@ -19,7 +19,7 @@ import forsyde.io.lib.hierarchy.platform.runtime.AbstractRuntime import forsyde.io.lib.hierarchy.ForSyDeHierarchy import forsyde.io.lib.hierarchy.platform.hardware.GenericMemoryModule import forsyde.io.lib.hierarchy.platform.hardware.GenericCommunicationModule -import idesyde.common.RuntimesAndProcessors +import idesyde.common.legacy.RuntimesAndProcessors trait PlatformRules { @@ -27,7 +27,7 @@ trait PlatformRules { models: Set[DesignModel], identified: Set[DecisionModel] ): (Set[RuntimesAndProcessors], Set[String]) = { - ForSyDeIdentificationUtils.toForSyDe(models) { model => + ForSyDeIdentificationUtils.toForSyDe(models){ model => var errors = mutable.Set[String]() var processingElements = Buffer[GenericProcessingModule]() var runtimeElements = Buffer[AbstractRuntime]() diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/SDFRules.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/SDFRules.scala similarity index 94% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/SDFRules.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/SDFRules.scala index 036bbd00..8d2de53d 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/SDFRules.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/SDFRules.scala @@ -1,11 +1,11 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import scala.jdk.CollectionConverters._ import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.forsydeio.ForSyDeDesignModel -import idesyde.common.SDFApplicationWithFunctions +import idesyde.forsydeio.legacy.ForSyDeDesignModel +import idesyde.common.legacy.SDFApplicationWithFunctions import scala.collection.mutable.Buffer import scala.collection.mutable import forsyde.io.lib.hierarchy.behavior.moc.sdf.SDFActor @@ -13,7 +13,7 @@ import forsyde.io.core.SystemGraph import forsyde.io.lib.hierarchy.implementation.functional.BufferLike import forsyde.io.lib.hierarchy.behavior.moc.sdf.SDFChannel import forsyde.io.lib.hierarchy.ForSyDeHierarchy -import idesyde.forsydeio.ForSyDeIdentificationUtils +import idesyde.forsydeio.legacy.ForSyDeIdentificationUtils trait SDFRules { diff --git a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/WorkloadRules.scala b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/WorkloadRules.scala similarity index 96% rename from scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/WorkloadRules.scala rename to scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/WorkloadRules.scala index 559f2d2f..77c3f584 100644 --- a/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/WorkloadRules.scala +++ b/scala-bridge-forsyde-io/src/main/scala/idesyde/forsydeio/legacy/WorkloadRules.scala @@ -1,4 +1,4 @@ -package idesyde.forsydeio +package idesyde.forsydeio.legacy import scala.jdk.StreamConverters._ import scala.jdk.CollectionConverters._ @@ -6,8 +6,8 @@ import scala.jdk.OptionConverters._ import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.common.CommunicatingAndTriggeredReactiveWorkload -import idesyde.forsydeio.ForSyDeIdentificationUtils +import idesyde.common.legacy.CommunicatingAndTriggeredReactiveWorkload +import idesyde.forsydeio.legacy.ForSyDeIdentificationUtils import scala.collection.mutable.Buffer import org.jgrapht.graph.AsSubgraph import org.jgrapht.alg.connectivity.ConnectivityInspector diff --git a/scala-choco/src/main/scala/idesyde/choco/CanSolveDepTasksToPartitionedMultiCore.scala b/scala-choco/src/main/scala/idesyde/choco/CanSolveDepTasksToPartitionedMultiCore.scala index 201ef221..e66fc08a 100644 --- a/scala-choco/src/main/scala/idesyde/choco/CanSolveDepTasksToPartitionedMultiCore.scala +++ b/scala-choco/src/main/scala/idesyde/choco/CanSolveDepTasksToPartitionedMultiCore.scala @@ -22,7 +22,7 @@ import org.chocosolver.solver.exception.ContradictionException import idesyde.choco.HasSingleProcessSingleMessageMemoryConstraints import idesyde.choco.HasActive4StageDuration import idesyde.identification.choco.interfaces.ChocoModelMixin -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore import idesyde.core.DecisionModel import idesyde.identification.choco.ChocoDecisionModel import idesyde.choco.HasDiscretizationToIntegers @@ -54,7 +54,7 @@ final class CanSolveDepTasksToPartitionedMultiCore ): (Model, Map[String, IntVar]) = { val chocoModel = Model() val timeValues = - (m.workload.periods ++ m.wcets.flatten.filter(_ > 0) ++ m.workload.relative_deadlines).sorted + (m.workload.periods ++ m.wcets.flatten.filter(_ > 0) ++ m.workload.relative_deadlines).sorted.filter(_ < Double.PositiveInfinity) val memoryValues = m.platform.hardware.storageSizes ++ m.workload.messagesMaxSizes ++ m.workload.processSizes @@ -120,7 +120,7 @@ final class CanSolveDepTasksToPartitionedMultiCore s"task_exec($t)", m.platform.hardware.processingElems.zipWithIndex .filter((_, j) => m.wcets(i)(j) > -1) - .filter((p, j) => m.wcets(i)(j) <= periods(i)) + .filter((p, j) => m.wcets(i)(j) <= m.workload.periods(i)) .map((m, j) => j) .toArray ) @@ -258,7 +258,7 @@ final class CanSolveDepTasksToPartitionedMultiCore // for each FP scheduler // rt >= bt + sum of all higher prio tasks in the same CPU postPartitionedFixedPrioriPreemtpiveConstraint(m.platform.runtimes.schedulers.zipWithIndex - .filter((s, j) => m.platform.runtimes.isFixedPriority(j)) + .filter((s, j) => m.platform.runtimes.is_fixed_priority(j)) .map((s, j) => j), chocoModel, priorities, @@ -275,7 +275,7 @@ final class CanSolveDepTasksToPartitionedMultiCore // for each SC scheduler m.workload.tasks.zipWithIndex.foreach((task, i) => { m.platform.runtimes.schedulers.zipWithIndex - .filter((s, j) => m.platform.runtimes.isCyclicExecutive(j)) + .filter((s, j) => m.platform.runtimes.is_cyclic_executive(j)) .foreach((s, j) => { postStaticCyclicExecutiveConstraint( chocoModel, @@ -359,7 +359,7 @@ final class CanSolveDepTasksToPartitionedMultiCore configuration: Explorer.Configuration ): ExplorationSolution = { val timeValues = - (m.workload.periods ++ m.wcets.flatten.filter(_ > 0) ++ m.workload.relative_deadlines) + (m.workload.periods ++ m.wcets.flatten.filter(_ > 0) ++ m.workload.relative_deadlines).filter(_ < Double.PositiveInfinity) val memoryValues = m.platform.hardware.storageSizes ++ m.workload.messagesMaxSizes ++ m.workload.processSizes diff --git a/scala-choco/src/main/scala/idesyde/choco/CanSolvePeriodicWorkloadAndSDFServersToMulticore.scala b/scala-choco/src/main/scala/idesyde/choco/CanSolvePeriodicWorkloadAndSDFServersToMulticore.scala index 66f35c33..34b8d91c 100644 --- a/scala-choco/src/main/scala/idesyde/choco/CanSolvePeriodicWorkloadAndSDFServersToMulticore.scala +++ b/scala-choco/src/main/scala/idesyde/choco/CanSolvePeriodicWorkloadAndSDFServersToMulticore.scala @@ -4,7 +4,7 @@ package idesyde.choco import org.chocosolver.solver.Model import org.chocosolver.solver.Solution -import idesyde.common.PeriodicWorkloadAndSDFServerToMultiCoreOld +import idesyde.common.legacy.PeriodicWorkloadAndSDFServerToMultiCoreOld import org.chocosolver.solver.search.loop.monitors.IMonitorContradiction import org.chocosolver.solver.exception.ContradictionException import org.chocosolver.solver.variables.IntVar @@ -22,8 +22,8 @@ import org.jgrapht.graph.DefaultDirectedGraph import org.jgrapht.graph.DefaultEdge import scala.collection.mutable.Buffer import org.jgrapht.Graph -import idesyde.common.PartitionedSharedMemoryMultiCore -import idesyde.common.SDFApplicationWithFunctions +import idesyde.common.legacy.PartitionedSharedMemoryMultiCore +import idesyde.common.legacy.SDFApplicationWithFunctions import org.chocosolver.solver.constraints.extension.Tuples import org.jgrapht.alg.shortestpath.FloydWarshallShortestPaths @@ -100,7 +100,7 @@ final class CanSolvePeriodicWorkloadAndSDFServersToMulticore s"processExecution($t)", m.platform.hardware.processingElems.zipWithIndex .filter((_, j) => m.wcets(i)(j) > -1) - .filter((_, j) => m.platform.runtimes.isFixedPriority(j)) + .filter((_, j) => m.platform.runtimes.is_fixed_priority(j)) .map((m, j) => j) .toArray )) @@ -111,7 +111,7 @@ final class CanSolvePeriodicWorkloadAndSDFServersToMulticore s"processExecution($t)", m.platform.hardware.processingElems.zipWithIndex .filter((_, j) => m.wcets(i)(j) > -1) - .filter((_, j) => m.platform.runtimes.isFixedPriority(j) || m.platform.runtimes.isBareMetal(j)) + .filter((_, j) => m.platform.runtimes.is_fixed_priority(j) || m.platform.runtimes.is_bare_metal(j)) .map((m, j) => j) .toArray ) @@ -285,7 +285,7 @@ final class CanSolvePeriodicWorkloadAndSDFServersToMulticore postPartitionedFixedPrioriPreemtpiveConstraint( m.platform.runtimes.schedulers.zipWithIndex - .filter((s, j) => m.platform.runtimes.isFixedPriority(j)) + .filter((s, j) => m.platform.runtimes.is_fixed_priority(j)) .map((s, j) => j), chocoModel, priorities, @@ -300,7 +300,7 @@ final class CanSolvePeriodicWorkloadAndSDFServersToMulticore responseTimes.toArray) // for each SC scheduler m.platform.runtimes.schedulers.zipWithIndex - .filter((s, j) => m.platform.runtimes.isCyclicExecutive(j)) + .filter((s, j) => m.platform.runtimes.is_cyclic_executive(j)) .foreach((s, j) => { postStaticCyclicExecutiveConstraint( chocoModel, @@ -564,7 +564,7 @@ final class CanSolvePeriodicWorkloadAndSDFServersToMulticore .toVector iter.toMap }).toMap - val utilizationPerRuntime = m.platform.runtimes.schedulers.zipWithIndex.filter((s, i) => m.platform.runtimes.isFixedPriority(i)).map((s, i) => + val utilizationPerRuntime = m.platform.runtimes.schedulers.zipWithIndex.filter((s, i) => m.platform.runtimes.is_fixed_priority(i)).map((s, i) => intVars.find(_.getName().startsWith(s"utilization($i)")).get.getValue().toDouble / 100.0 ) ExplorationSolution( diff --git a/scala-choco/src/main/scala/idesyde/choco/CanSolveSDFToTiledMultiCore.scala b/scala-choco/src/main/scala/idesyde/choco/CanSolveSDFToTiledMultiCore.scala index 7d0d4f28..74b30c2b 100644 --- a/scala-choco/src/main/scala/idesyde/choco/CanSolveSDFToTiledMultiCore.scala +++ b/scala-choco/src/main/scala/idesyde/choco/CanSolveSDFToTiledMultiCore.scala @@ -21,7 +21,7 @@ import idesyde.identification.choco.interfaces.ChocoModelMixin import org.chocosolver.solver.search.loop.monitors.IMonitorContradiction import org.chocosolver.solver.exception.ContradictionException import scala.collection.mutable.Buffer -import idesyde.common.SDFToTiledMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore import org.chocosolver.solver.objective.OptimizationPolicy import idesyde.identification.choco.models.sdf.CompactingMultiCoreMapping import org.jgrapht.graph.SimpleDirectedGraph diff --git a/scala-choco/src/main/scala/idesyde/choco/ChocoExplorationModule.scala b/scala-choco/src/main/scala/idesyde/choco/ChocoExplorationModule.scala index b129db72..fb151314 100644 --- a/scala-choco/src/main/scala/idesyde/choco/ChocoExplorationModule.scala +++ b/scala-choco/src/main/scala/idesyde/choco/ChocoExplorationModule.scala @@ -6,11 +6,11 @@ import upickle.default._ import idesyde.blueprints.StandaloneModule import idesyde.core.DecisionModel -import idesyde.common.SDFToTiledMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore import idesyde.choco.ChocoExplorer import spire.math.Rational -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore -import idesyde.common.PeriodicWorkloadAndSDFServerToMultiCoreOld +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.PeriodicWorkloadAndSDFServerToMultiCoreOld import idesyde.core.OpaqueDecisionModel import java.util.Optional diff --git a/scala-choco/src/main/scala/idesyde/choco/ChocoExplorer.scala b/scala-choco/src/main/scala/idesyde/choco/ChocoExplorer.scala index f5e58706..cc829402 100644 --- a/scala-choco/src/main/scala/idesyde/choco/ChocoExplorer.scala +++ b/scala-choco/src/main/scala/idesyde/choco/ChocoExplorer.scala @@ -23,62 +23,70 @@ import org.chocosolver.solver.search.loop.monitors.IMonitorSolution import org.chocosolver.solver.search.loop.monitors.IMonitorContradiction import idesyde.exploration.choco.explorers.ParetoMinimizationBrancher import spire.math.Rational -import idesyde.common.SDFToTiledMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore import idesyde.choco.ChocoExplorableOps._ -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore -import idesyde.common.PeriodicWorkloadAndSDFServerToMultiCoreOld +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.PeriodicWorkloadAndSDFServerToMultiCoreOld import idesyde.core.Explorer import idesyde.core.ExplorationBidding import idesyde.core.ExplorationSolution import org.chocosolver.solver.exception.ContradictionException import java.util.concurrent.CopyOnWriteArraySet +import idesyde.common.legacy.CommonModule.tryCast +import org.chocosolver.solver.search.loop.monitors.SearchMonitorList class ChocoExplorer extends Explorer: override def bid( - explorers: java.util.Set[Explorer], decisionModel: DecisionModel ): ExplorationBidding = { - val canExplore = decisionModel match - // case sdfToMemMapped: AperiodicAsynchronousDataflowToPartitionedMemoryMappableMulticore => true - // case sdfToTiled: AperiodicAsynchronousDataflowToPartitionedTiledMulticore => true - case sdf: SDFToTiledMultiCore => true - case workload: PeriodicWorkloadToPartitionedSharedMultiCore => true - case workloadAndSDF: PeriodicWorkloadAndSDFServerToMultiCoreOld => true - case c: ChocoDecisionModel => true - case _ => false - val objectives: Set[String] = decisionModel match { - case sdf: SDFToTiledMultiCore => - sdf.sdfApplications.minimumActorThroughputs.zipWithIndex - .filter((th, i) => th > 0.0) - .map((th, i) => "invThroughput(" + sdf.sdfApplications.actorsIdentifiers(i) + ")") - .toSet + "nUsedPEs" - case workload: PeriodicWorkloadToPartitionedSharedMultiCore => Set("nUsedPEs") - case workloadAndSDF: PeriodicWorkloadAndSDFServerToMultiCoreOld => - workloadAndSDF.tasksAndSDFs.sdfApplications.minimumActorThroughputs.zipWithIndex - .filter((th, i) => th > 0.0) - .map((th, i) => - "invThroughput(" + workloadAndSDF.tasksAndSDFs.sdfApplications - .actorsIdentifiers(i) + ")" + val bidding = decisionModel.category() match { + case "SDFToTiledMultiCore" => { + tryCast(decisionModel, classOf[SDFToTiledMultiCore]) { sdf => + ExplorationBidding( + true, + true, + 1.0, + (sdf.sdfApplications.minimumActorThroughputs.zipWithIndex + .filter((th, i) => th > 0.0) + .map((th, i) => "invThroughput(" + sdf.sdfApplications.actorsIdentifiers(i) + ")") + .toSet + "nUsedPEs").asJava, + java.util.Map.of("time-to-first", 100.0) ) - .toSet + "nUsedPEs" - case _ => Set() + } + } + case "PeriodicWorkloadToPartitionedSharedMultiCore" => { + tryCast(decisionModel, classOf[PeriodicWorkloadToPartitionedSharedMultiCore]) { workload => + ExplorationBidding( + true, + true, + 1.0, + Set("nUsedPEs").asJava, + java.util.Map.of("time-to-first", 100.0) + ) + } + } + case "PeriodicWorkloadAndSDFServerToMultiCoreOld" => { + tryCast(decisionModel, classOf[PeriodicWorkloadAndSDFServerToMultiCoreOld]) { + workloadAndSDF => + ExplorationBidding( + true, + true, + 1.0, + (workloadAndSDF.tasksAndSDFs.sdfApplications.minimumActorThroughputs.zipWithIndex + .filter((th, i) => th > 0.0) + .map((th, i) => + "invThroughput(" + workloadAndSDF.tasksAndSDFs.sdfApplications + .actorsIdentifiers(i) + ")" + ) + .toSet + "nUsedPEs").asJava, + java.util.Map.of("time-to-first", 100.0) + ) + } + } + case _ => None } - // println(decisionModel.category()) - // println(ExplorationBidding( - // canExplore, - // true, - // 1.0, - // objectives.asJava, - // java.util.Map.of("time-to-first", 100.0) - // )) - ExplorationBidding( - canExplore, - true, - 1.0, - objectives.asJava, - java.util.Map.of("time-to-first", 100.0) - ) + bidding.getOrElse(ExplorationBidding(false, false, 0.0, Set().asJava, java.util.Map.of())) } // override def availableCriterias(decisionModel: DecisionModel): Set[ExplorationCriteria] = @@ -144,13 +152,13 @@ class ChocoExplorer extends Explorer: configuration ) var solver = model.getSolver() - if (configuration.improvementTimeOutInSecs > 0L) { - solver.limitTime(configuration.improvementTimeOutInSecs * 1000L) - } if (configuration.improvementIterations > 0L) { solver.limitFail(configuration.improvementIterations) solver.limitBacktrack(configuration.improvementIterations) } + if (configuration.improvementTimeOutInSecs > 0L) { + solver.limitTime(configuration.improvementTimeOutInSecs * 1000L) + } val chocoSolution = solver.findSolution() if (chocoSolution != null) { @@ -231,96 +239,62 @@ class ChocoExplorer extends Explorer: previousSolutions: java.util.Set[ExplorationSolution], configuration: Explorer.Configuration ): Stream[ExplorationSolution] = { - var llist = decisionModel match - case sdf: SDFToTiledMultiCore => - exploreChocoExplorable( - sdf, - previousSolutions.asScala - .filter(sol => sol.solved().isInstanceOf[SDFToTiledMultiCore]) - .map(sol => - ExplorationSolution(sol.objectives(), sol.solved().asInstanceOf[SDFToTiledMultiCore]) - ) - .toSet, - configuration - )(using CanSolveSDFToTiledMultiCore()) - case workload: PeriodicWorkloadToPartitionedSharedMultiCore => - exploreChocoExplorable( - workload, - previousSolutions.asScala - .filter(sol => sol.solved().isInstanceOf[PeriodicWorkloadToPartitionedSharedMultiCore]) - .map(sol => - ExplorationSolution( - sol.objectives(), - sol.solved().asInstanceOf[PeriodicWorkloadToPartitionedSharedMultiCore] + var llist = decisionModel.category() match + case "SDFToTiledMultiCore" => + tryCast(decisionModel, classOf[SDFToTiledMultiCore]) { sdf => + exploreChocoExplorable( + sdf, + previousSolutions.asScala + .filter(sol => sol.solved().isInstanceOf[SDFToTiledMultiCore]) + .map(sol => + ExplorationSolution( + sol.objectives(), + sol.solved().asInstanceOf[SDFToTiledMultiCore] + ) ) - ) - .toSet, - configuration - )(using CanSolveDepTasksToPartitionedMultiCore()) - case workloadAndSDF: PeriodicWorkloadAndSDFServerToMultiCoreOld => - exploreChocoExplorable( - workloadAndSDF, - previousSolutions.asScala - .filter(sol => sol.solved().isInstanceOf[PeriodicWorkloadAndSDFServerToMultiCoreOld]) - .map(sol => - ExplorationSolution( - sol.objectives(), - sol.solved().asInstanceOf[PeriodicWorkloadAndSDFServerToMultiCoreOld] + .toSet, + configuration + )(using CanSolveSDFToTiledMultiCore()) + } + case "PeriodicWorkloadToPartitionedSharedMultiCore" => + tryCast(decisionModel, classOf[PeriodicWorkloadToPartitionedSharedMultiCore]) { workload => + exploreChocoExplorable( + workload, + previousSolutions.asScala + .filter(sol => + sol.solved().isInstanceOf[PeriodicWorkloadToPartitionedSharedMultiCore] ) - ) - .toSet, - configuration - )(using CanSolvePeriodicWorkloadAndSDFServersToMulticore()) - // case solvable: ChocoDecisionModel => - // val solver = solvable.chocoModel.getSolver - // val isOptimization = solvable.modelMinimizationObjectives.size > 0 - // val paretoMinimizer = ParetoMinimizationBrancher(solvable.modelMinimizationObjectives) - // // lazy val paretoMaximizer = ParetoMaximizer( - // // solvable.modelMinimizationObjectives.map(o => solvable.chocoModel.intMinusView(o)) - // // ) - // // var lastParetoFrontValues = solvable.modelMinimizationObjectives.map(_.getUB()) - // // var lastParetoFrontSize = 0 - // if (isOptimization) { - // if (solvable.modelMinimizationObjectives.size == 1) { - // solvable.chocoModel.setObjective( - // false, - // solvable.modelMinimizationObjectives.head - // ) - // } - // solver.plugMonitor(paretoMinimizer) - // solvable.chocoModel.post(new Constraint("paretoOptConstraint", paretoMinimizer)) - // // val objFunc = getLinearizedObj(solvable) - // // solvable.chocoModel.setObjective(false, objFunc) - // // strategies +:= Search.bestBound(Search.minDomLBSearch(objFunc)) - // } - // // solver.addStopCriterion(SolutionCounter(solvable.chocoModel, 2L)) - // if (!solvable.strategies.isEmpty) { - // solver.setSearch(solvable.strategies: _*) - // } - // if (solvable.shouldLearnSignedClauses) { - // solver.setLearningSignedClauses - // } - // if (solvable.shouldRestartOnSolution) { - // solver.setNoGoodRecordingFromRestarts - // solver.setRestartOnSolutions - // } - // if (explorationTotalTimeOutInSecs > 0L) { - // logger.debug( - // s"setting total exploration timeout to ${explorationTotalTimeOutInSecs} seconds" - // ) - // solver.limitTime(explorationTotalTimeOutInSecs * 1000L) - // } - // LazyList - // .continually(solver.solve()) - // .takeWhile(feasible => feasible) - // .map(_ => { - // solver.defaultSolution() - // }) - // .flatMap(paretoSolution => { - // solvable.rebuildFromChocoOutput(paretoSolution) - // }) - case _ => LazyList.empty - val iter = llist.iterator + .map(sol => + ExplorationSolution( + sol.objectives(), + sol.solved().asInstanceOf[PeriodicWorkloadToPartitionedSharedMultiCore] + ) + ) + .toSet, + configuration + )(using CanSolveDepTasksToPartitionedMultiCore()) + } + case "PeriodicWorkloadAndSDFServerToMultiCoreOld" => + tryCast(decisionModel, classOf[PeriodicWorkloadAndSDFServerToMultiCoreOld]) { + workloadAndSDF => + exploreChocoExplorable( + workloadAndSDF, + previousSolutions.asScala + .filter(sol => + sol.solved().isInstanceOf[PeriodicWorkloadAndSDFServerToMultiCoreOld] + ) + .map(sol => + ExplorationSolution( + sol.objectives(), + sol.solved().asInstanceOf[PeriodicWorkloadAndSDFServerToMultiCoreOld] + ) + ) + .toSet, + configuration + )(using CanSolvePeriodicWorkloadAndSDFServersToMulticore()) + } + case _ => None + val iter = llist.map(_.iterator).getOrElse(Iterator.empty) val foundObjectives = CopyOnWriteArraySet[java.util.Map[String, java.lang.Double]]() Stream .generate(() => { diff --git a/scala-choco/src/main/scala/idesyde/choco/HasActive4StageDuration.scala b/scala-choco/src/main/scala/idesyde/choco/HasActive4StageDuration.scala index 20eb84de..1e0031ec 100644 --- a/scala-choco/src/main/scala/idesyde/choco/HasActive4StageDuration.scala +++ b/scala-choco/src/main/scala/idesyde/choco/HasActive4StageDuration.scala @@ -5,7 +5,7 @@ import org.chocosolver.solver.variables.IntVar import org.chocosolver.solver.variables.BoolVar import org.chocosolver.solver.constraints.`extension`.Tuples import org.chocosolver.solver.Model -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore trait HasActive4StageDuration extends HasUtils { diff --git a/scala-choco/src/main/scala/idesyde/choco/HasDiscretizationToIntegers.scala b/scala-choco/src/main/scala/idesyde/choco/HasDiscretizationToIntegers.scala index e98feb4a..6336ee6b 100644 --- a/scala-choco/src/main/scala/idesyde/choco/HasDiscretizationToIntegers.scala +++ b/scala-choco/src/main/scala/idesyde/choco/HasDiscretizationToIntegers.scala @@ -14,7 +14,6 @@ trait HasDiscretizationToIntegers extends HasUtils { // r += 1 // println("asd " + t + " - " + r) // } - // println((t, step)) fracT.div(t, step).toDouble.ceil.toInt } diff --git a/scala-choco/src/main/scala/idesyde/choco/HasSDFSchedulingAnalysisAndConstraints.scala b/scala-choco/src/main/scala/idesyde/choco/HasSDFSchedulingAnalysisAndConstraints.scala index 3f00bb4e..823c80fd 100644 --- a/scala-choco/src/main/scala/idesyde/choco/HasSDFSchedulingAnalysisAndConstraints.scala +++ b/scala-choco/src/main/scala/idesyde/choco/HasSDFSchedulingAnalysisAndConstraints.scala @@ -15,7 +15,7 @@ import idesyde.choco.HasTileAsyncInterconnectCommunicationConstraints import idesyde.choco.HasSingleProcessSingleMessageMemoryConstraints import idesyde.choco.HasDiscretizationToIntegers import scala.collection.mutable.Buffer -import idesyde.common.SDFToTiledMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore import idesyde.identification.choco.models.sdf.StreamingJobsThroughputPropagator import org.jgrapht.alg.connectivity.ConnectivityInspector import org.jgrapht.traverse.TopologicalOrderIterator diff --git a/scala-choco/src/main/scala/idesyde/identification/choco/rules/ChocoRules.scala b/scala-choco/src/main/scala/idesyde/identification/choco/rules/ChocoRules.scala index 18605c35..bf836c57 100644 --- a/scala-choco/src/main/scala/idesyde/identification/choco/rules/ChocoRules.scala +++ b/scala-choco/src/main/scala/idesyde/identification/choco/rules/ChocoRules.scala @@ -3,9 +3,9 @@ package idesyde.identification.choco.rules import idesyde.core.DesignModel import idesyde.core.DecisionModel import spire.math.Rational -import idesyde.common.SDFToTiledMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore import idesyde.choco.CanSolveDepTasksToPartitionedMultiCore -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore trait ChocoRules { diff --git a/scala-common/src/main/scala/idesyde/common/MixedRules.scala b/scala-common/src/main/scala/idesyde/common/MixedRules.scala deleted file mode 100644 index 5c7d1b09..00000000 --- a/scala-common/src/main/scala/idesyde/common/MixedRules.scala +++ /dev/null @@ -1,193 +0,0 @@ -package idesyde.common - -import idesyde.core.DecisionModel -import idesyde.core.DesignModel -import scala.collection.mutable - -trait MixedRules { - - def identTaskdAndSDFServer( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[PeriodicWorkloadAndSDFServers], Set[String]) = { - var errors = mutable.Set[String]() - val sdfDecisionModel = identified - .filter(_.isInstanceOf[SDFApplicationWithFunctions]) - .map(_.asInstanceOf[SDFApplicationWithFunctions]) - for (a <- sdfDecisionModel) { - if (!a.isConsistent) { - errors += s"identTaskdAndSDFServer: SDFApplication containing ${a.actorsIdentifiers.head} is inconsistent. Ignoring it." - } - } - val taskDecisionModel = identified - .filter(_.isInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) - .map(_.asInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) - ( - sdfDecisionModel - .filter(_.isConsistent) - .flatMap(a => - taskDecisionModel.map(b => - PeriodicWorkloadAndSDFServers( - sdfApplications = a, - workload = b - ) - ) - ), - errors.toSet - ) - - } - - def identSDFToTiledMultiCore( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[SDFToTiledMultiCore], Set[String]) = { - var errors = mutable.Set[String]() - val app = identified - .filter(_.isInstanceOf[SDFApplicationWithFunctions]) - .map(_.asInstanceOf[SDFApplicationWithFunctions]) - if (app.isEmpty) { - errors += "identSDFToTiledMultiCore: no SDFApplicationWithFunctions found" - } - for (a <- app) { - if (!a.isConsistent) { - errors += s"identSDFToTiledMultiCore: SDFApplication containing ${a.actorsIdentifiers.head} is inconsistent. Ignoring it." - } - } - val plat = identified - .filter(_.isInstanceOf[SchedulableTiledMultiCore]) - .map(_.asInstanceOf[SchedulableTiledMultiCore]) - if (plat.isEmpty) { - errors += "identSDFToTiledMultiCore: no SchedulableTiledMultiCore found" - } - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - ( - app - .filter(_.isConsistent) - .flatMap(a => - plat.map(p => - SDFToTiledMultiCore( - sdfApplications = a, - platform = p, - processMappings = Vector.empty, - messageMappings = Vector.empty, - schedulerSchedules = Vector.empty, - messageSlotAllocations = Vector.empty, - actorThroughputs = Vector.empty - ) - ) - ), - errors.toSet - ) - } - - def identSDFToPartitionedSharedMemory( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[SDFToPartitionedSharedMemory], Set[String]) = { - var errors = mutable.Set[String]() - val app = identified - .filter(_.isInstanceOf[SDFApplicationWithFunctions]) - .map(_.asInstanceOf[SDFApplicationWithFunctions]) // only go forward if the SDF is consistent - for (a <- app) { - if (!a.isConsistent) { - errors += s"identSDFToPartitionedSharedMemory: SDFApplication containing ${a.actorsIdentifiers.head} is inconsistent. Ignoring it." - } - } - val plat = identified - .filter(_.isInstanceOf[PartitionedSharedMemoryMultiCore]) - .map(_.asInstanceOf[PartitionedSharedMemoryMultiCore]) - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - ( - app - .filter(_.isConsistent) - .flatMap(a => - plat.map(p => - SDFToPartitionedSharedMemory( - sdfApplications = a, - platform = p, - processMappings = Vector.empty, - memoryMappings = Vector.empty, - messageSlotAllocations = Vector.empty - ) - ) - ), - errors.toSet - ) - } - - def identPeriodicWorkloadToPartitionedSharedMultiCore( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[PeriodicWorkloadToPartitionedSharedMultiCore], Set[String]) = { - val app = identified - .filter(_.isInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) - .map(_.asInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) - val plat = identified - .filter(_.isInstanceOf[PartitionedSharedMemoryMultiCore]) - .map(_.asInstanceOf[PartitionedSharedMemoryMultiCore]) - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - val (m, e) = app - .flatMap(a => - plat - .map(p => - val potential = PeriodicWorkloadToPartitionedSharedMultiCore( - workload = a, - platform = p, - processMappings = Vector.empty, - processSchedulings = Vector.empty, - channelMappings = Vector.empty, - channelSlotAllocations = Map(), - maxUtilizations = Map() - ) - if ( - potential.wcets.zipWithIndex - .forall((wi, i) => wi.exists(w => w > 0.0 && w <= a.relative_deadlines(i))) - ) { - (Some(potential), None) - } else { - ( - None, - Some( - "identPeriodicWorkloadToPartitionedSharedMultiCore: not all tasks are mappable to the platform" - ) - ) - } - ) - ) - .unzip - (m.flatten, e.flatten) - } - - def identTaksAndSDFServerToMultiCore( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[PeriodicWorkloadAndSDFServerToMultiCoreOld], Set[String]) = { - val app = identified - .filter(_.isInstanceOf[PeriodicWorkloadAndSDFServers]) - .map(_.asInstanceOf[PeriodicWorkloadAndSDFServers]) - val plat = identified - .filter(_.isInstanceOf[PartitionedSharedMemoryMultiCore]) - .map(_.asInstanceOf[PartitionedSharedMemoryMultiCore]) - .filter(_.runtimes.isFixedPriority.count(_ == true) > 0) - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - ( - app.flatMap(a => - plat.map(p => - PeriodicWorkloadAndSDFServerToMultiCoreOld( - tasksAndSDFs = a, - platform = p, - processesSchedulings = Vector.empty, - processesMappings = Vector.empty, - messagesMappings = Vector.empty, - messageSlotAllocations = Map.empty, - sdfServerUtilization = Vector.empty[Double], - sdfOrderBasedSchedules = p.runtimes.schedulers.map(p => Vector.empty) - ) - ) - ), - Set() - ) - } - -} diff --git a/scala-common/src/main/scala/idesyde/common/PlatformRules.scala b/scala-common/src/main/scala/idesyde/common/PlatformRules.scala deleted file mode 100644 index 85f4492d..00000000 --- a/scala-common/src/main/scala/idesyde/common/PlatformRules.scala +++ /dev/null @@ -1,126 +0,0 @@ -package idesyde.common - -import scala.jdk.CollectionConverters._ - -import idesyde.core.DesignModel -import idesyde.core.DecisionModel -import scala.collection.mutable -import org.jgrapht.alg.shortestpath.FloydWarshallShortestPaths - -trait PlatformRules { - - def identSchedulableTiledMultiCore( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[SchedulableTiledMultiCore], Set[String]) = { - val runtimes = identified - .filter(_.isInstanceOf[PartitionedCoresWithRuntimes]) - .map(_.asInstanceOf[PartitionedCoresWithRuntimes]) - val plat = identified - .filter(_.isInstanceOf[TiledMultiCoreWithFunctions]) - .map(_.asInstanceOf[TiledMultiCoreWithFunctions]) - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - ( - runtimes.flatMap(r => plat.map(p => SchedulableTiledMultiCore(hardware = p, runtimes = r))), - Set() - ) - } - - def identPartitionedSharedMemoryMultiCore( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[PartitionedSharedMemoryMultiCore], Set[String]) = { - val runtimes = identified - .filter(_.isInstanceOf[PartitionedCoresWithRuntimes]) - .map(_.asInstanceOf[PartitionedCoresWithRuntimes]) - val plat = identified - .filter(_.isInstanceOf[SharedMemoryMultiCore]) - .map(_.asInstanceOf[SharedMemoryMultiCore]) - // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) - ( - runtimes.flatMap(r => - plat.map(p => PartitionedSharedMemoryMultiCore(hardware = p, runtimes = r)) - ), - Set() - ) - } - - def identTiledFromShared( - models: Set[DesignModel], - identified: Set[DecisionModel] - ): (Set[TiledMultiCoreWithFunctions], Set[String]) = { - val plats = identified - .filter(_.isInstanceOf[SharedMemoryMultiCore]) - .map(_.asInstanceOf[SharedMemoryMultiCore]) - var tiledPlats = mutable.Set[TiledMultiCoreWithFunctions]() - var errors = mutable.Set[String]() - for (plat <- plats) { - val isTiled = plat.communicationElems.forall(p => - plat.topology - .outgoingEdgesOf(p) - .asScala - .map(plat.topology.getEdgeTarget) - .count(e => plat.storageElems.contains(e) || plat.processingElems.contains(e)) <= 2 - ) && - plat.storageElems.forall(p => - plat.topology - .outgoingEdgesOf(p) - .asScala - .map(plat.topology.getEdgeTarget) - .count(e => plat.communicationElems.contains(e)) <= 1 - ) && - plat.processingElems.length == plat.storageElems.length - if (isTiled) { - val shortestPaths = FloydWarshallShortestPaths(plat.topology) - val tiledMemories = plat.processingElems.map(pe => - plat.storageElems.minBy(me => - // plat.topology.get(pe).shortestPathTo(plat.topology.get(me)) match { - // case Some(path) => path.size - // case None => plat.communicationElems.length + 1 - // } - val path = shortestPaths.getPath(pe, me) - if (path != null) { - path.getLength() - } else { - plat.communicationElems.length + 1 - } - ) - ) - val tiledNI = plat.processingElems.map(pe => - plat.communicationElems.minBy(ce => - // plat.topology.get(pe).shortestPathTo(plat.topology.get(ce)) match { - // case Some(value) => value.size - // case None => plat.topology.nodes.size - // } - val path = shortestPaths.getPath(pe, ce) - if (path != null) { - path.getLength() - } else { - plat.communicationElems.length + 1 - } - ) - ) - val routers = plat.communicationElems.filterNot(tiledNI.contains) - tiledPlats += TiledMultiCoreWithFunctions( - processors = plat.processingElems, - memories = tiledMemories, - networkInterfaces = tiledNI, - routers = routers, - interconnectTopologySrcs = plat.topologySrcs, - interconnectTopologyDsts = plat.topologyDsts, - processorsProvisions = plat.processorsProvisions, - processorsFrequency = plat.processorsFrequency, - tileMemorySizes = - tiledMemories.map(me => plat.storageSizes(plat.storageElems.indexOf(me))), - communicationElementsMaxChannels = plat.communicationElementsMaxChannels, - communicationElementsBitPerSecPerChannel = plat.communicationElementsBitPerSecPerChannel, - preComputedPaths = plat.preComputedPaths - ) - } else { - errors += s"identTiledFromShared: The shared memory platform containing processing element ${plat.processingElems.head} is not tiled." - } - } - (tiledPlats.toSet, errors.toSet) - } - -} diff --git a/scala-common/src/main/scala/idesyde/common/AnalysedSDFApplication.scala b/scala-common/src/main/scala/idesyde/common/legacy/AnalysedSDFApplication.scala similarity index 94% rename from scala-common/src/main/scala/idesyde/common/AnalysedSDFApplication.scala rename to scala-common/src/main/scala/idesyde/common/legacy/AnalysedSDFApplication.scala index f573cff0..a60f3a81 100644 --- a/scala-common/src/main/scala/idesyde/common/AnalysedSDFApplication.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/AnalysedSDFApplication.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import upickle.default.* import idesyde.core.DecisionModel diff --git a/scala-common/src/main/scala/idesyde/common/AperiodicAsynchronousDataflow.scala b/scala-common/src/main/scala/idesyde/common/legacy/AperiodicAsynchronousDataflow.scala similarity index 96% rename from scala-common/src/main/scala/idesyde/common/AperiodicAsynchronousDataflow.scala rename to scala-common/src/main/scala/idesyde/common/legacy/AperiodicAsynchronousDataflow.scala index 8ddb54d4..643420ec 100644 --- a/scala-common/src/main/scala/idesyde/common/AperiodicAsynchronousDataflow.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/AperiodicAsynchronousDataflow.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import upickle.default.* import idesyde.core.DecisionModel diff --git a/scala-common/src/main/scala/idesyde/common/ApplicationRules.scala b/scala-common/src/main/scala/idesyde/common/legacy/ApplicationRules.scala similarity index 79% rename from scala-common/src/main/scala/idesyde/common/ApplicationRules.scala rename to scala-common/src/main/scala/idesyde/common/legacy/ApplicationRules.scala index 6d2a01d9..318b775c 100644 --- a/scala-common/src/main/scala/idesyde/common/ApplicationRules.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/ApplicationRules.scala @@ -1,21 +1,17 @@ -package idesyde.common +package idesyde.common.legacy import idesyde.core.DesignModel import idesyde.core.DecisionModel -import idesyde.common.AnalysedSDFApplication +import idesyde.common.legacy.AnalysedSDFApplication +import idesyde.common.legacy.CommonModule.tryCast trait ApplicationRules { def identCommonSDFApplication( models: Set[DesignModel], identified: Set[DecisionModel] ): (Set[SDFApplication], Set[String]) = { - ( - identified - .flatMap(_ match { - case m: SDFApplicationWithFunctions => Some(m) - case _ => None - }) - .map(sdfWithFunctions => { + tryCast(identified, classOf[SDFApplicationWithFunctions]) { filtered => + val proper = filtered.map(sdfWithFunctions => { val actors = sdfWithFunctions.actorsIdentifiers.toSet val channels = sdfWithFunctions.channelsIdentifiers.toSet SDFApplication( @@ -47,50 +43,51 @@ trait ApplicationRules { .map((src, dst, channels, msize, prod, cons, toks) => channels), chain_maximum_latency = Map() ) - }), - Set() - ) + }) + ( + proper, + Set() + ) + } } def identAnalysedSDFApplication( models: Set[DesignModel], identified: Set[DecisionModel] ): (Set[AnalysedSDFApplication], Set[String]) = { - identified - .flatMap(_ match { - case m: SDFApplicationWithFunctions => Some(m) - case _ => None - }) - .flatMap(sdfWithFunctions => - identified.flatMap(_ match { - case m: SDFApplication => - if (m.actors_identifiers == sdfWithFunctions.actorsIdentifiers.toSet) { - Some(sdfWithFunctions, m) - } else None - case _ => None + tryCast(identified, classOf[SDFApplicationWithFunctions]) { filtered => + filtered + .flatMap(sdfWithFunctions => + identified.flatMap(_ match { + case m: SDFApplication => + if (m.actors_identifiers == sdfWithFunctions.actorsIdentifiers.toSet) { + Some(sdfWithFunctions, m) + } else None + case _ => None + }) + ) + .map((sdfWithFunctions, m) => { + if (sdfWithFunctions.isConsistent) { + ( + Option( + AnalysedSDFApplication( + sdfWithFunctions.topologicalAndHeavyJobOrdering.map((a, q) => a), + sdfWithFunctions.actorsIdentifiers + .zip(sdfWithFunctions.sdfRepetitionVectors.map(_.toLong)) + .toMap, + m + ) + ), + None + ) + } else { + (None, Option("identAnalyzedSDFApplication: SDF is not consistent")) + } }) - ) - .map((sdfWithFunctions, m) => { - if (sdfWithFunctions.isConsistent) { - ( - Option( - AnalysedSDFApplication( - sdfWithFunctions.topologicalAndHeavyJobOrdering.map((a, q) => a), - sdfWithFunctions.actorsIdentifiers - .zip(sdfWithFunctions.sdfRepetitionVectors.map(_.toLong)) - .toMap, - m - ) - ), - None - ) - } else { - (None, Option("identAnalyzedSDFApplication: SDF is not consistent")) - } - }) - .foldLeft((Set(), Set()))((a, b) => - (b._1.map(a._1 + _).getOrElse(a._1), b._2.map(a._2 + _).getOrElse(a._2)) - ) + .foldLeft((Set(), Set()))((a, b) => + (b._1.map(a._1 + _).getOrElse(a._1), b._2.map(a._2 + _).getOrElse(a._2)) + ) + } } // def identAperiodicAsynchronousDataflow( diff --git a/scala-common/src/main/scala/idesyde/common/CommonModule.scala b/scala-common/src/main/scala/idesyde/common/legacy/CommonModule.scala similarity index 73% rename from scala-common/src/main/scala/idesyde/common/CommonModule.scala rename to scala-common/src/main/scala/idesyde/common/legacy/CommonModule.scala index d8dea464..8c342047 100644 --- a/scala-common/src/main/scala/idesyde/common/CommonModule.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/CommonModule.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.OptionConverters._ import scala.jdk.CollectionConverters._ @@ -8,18 +8,18 @@ import upickle.default.* import idesyde.blueprints.StandaloneModule import idesyde.core.DecisionModel import idesyde.core.DesignModel -import idesyde.common.SDFApplicationWithFunctions -import idesyde.common.TiledMultiCoreWithFunctions -import idesyde.common.PartitionedCoresWithRuntimes -import idesyde.common.SchedulableTiledMultiCore -import idesyde.common.SDFToTiledMultiCore -import idesyde.common.SharedMemoryMultiCore -import idesyde.common.CommunicatingAndTriggeredReactiveWorkload -import idesyde.common.PartitionedSharedMemoryMultiCore -import idesyde.common.PeriodicWorkloadToPartitionedSharedMultiCore -import idesyde.common.PeriodicWorkloadAndSDFServers +import idesyde.common.legacy.SDFApplicationWithFunctions +import idesyde.common.legacy.TiledMultiCoreWithFunctions +import idesyde.common.legacy.PartitionedCoresWithRuntimes +import idesyde.common.legacy.SchedulableTiledMultiCore +import idesyde.common.legacy.SDFToTiledMultiCore +import idesyde.common.legacy.SharedMemoryMultiCore +import idesyde.common.legacy.CommunicatingAndTriggeredReactiveWorkload +import idesyde.common.legacy.PartitionedSharedMemoryMultiCore +import idesyde.common.legacy.PeriodicWorkloadToPartitionedSharedMultiCore +import idesyde.common.legacy.PeriodicWorkloadAndSDFServers import idesyde.core.IdentificationRule -import idesyde.common.AnalysedSDFApplication +import idesyde.common.legacy.AnalysedSDFApplication import idesyde.core.OpaqueDecisionModel import java.{util => ju} import idesyde.core.IdentificationResult @@ -138,4 +138,41 @@ object CommonModule } } + inline def tryCast[M <: DecisionModel, B](models: Set[DecisionModel], cls: Class[M])( + inline body: Set[M] => B + )(using ReadWriter[M]): B = { + val relevant = models + .flatMap(_ match { + case model: M => Some(model) + case opaqueModel: OpaqueDecisionModel => + if (opaqueModel.category() == cls.getSimpleName()) { + try { + opaqueModel.asJsonString().map(read[M](_)).toScala + } catch { + case _: Throwable => None + } + } else None + case _ => None + }) + body(relevant) + } + + inline def tryCast[M <: DecisionModel, B](model: DecisionModel, cls: Class[M])( + inline body: M => B + )(using ReadWriter[M]): Option[B] = { + val relevant = model match { + case model: M => Some(model) + case opaqueModel: OpaqueDecisionModel => + if (opaqueModel.category() == cls.getSimpleName()) { + try { + opaqueModel.asJsonString().map(read[M](_)).toScala + } catch { + case _: Throwable => None + } + } else None + case _ => None + } + relevant.map(body(_)) + } + } diff --git a/scala-common/src/main/scala/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.scala b/scala-common/src/main/scala/idesyde/common/legacy/CommunicatingAndTriggeredReactiveWorkload.scala similarity index 84% rename from scala-common/src/main/scala/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.scala rename to scala-common/src/main/scala/idesyde/common/legacy/CommunicatingAndTriggeredReactiveWorkload.scala index 7460e4fc..9216781d 100644 --- a/scala-common/src/main/scala/idesyde/common/CommunicatingAndTriggeredReactiveWorkload.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/CommunicatingAndTriggeredReactiveWorkload.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import spire.math.Rational // import scalax.collection.immutable.Graph @@ -29,14 +29,14 @@ final case class CommunicatingAndTriggeredReactiveWorkload( val offsets_numerator: Vector[Long], val offsets_denominator: Vector[Long], val upsamples: Vector[String], - val upsampleRepetitiveHolds: Vector[Long], - val upsampleInitialHolds: Vector[Long], + val upsample_repetitive_holds: Vector[Long], + val upsample_initial_holds: Vector[Long], val downsamples: Vector[String], - val downampleRepetitiveSkips: Vector[Long], - val downampleInitialSkips: Vector[Long], - val triggerGraphSrc: Vector[String], - val triggerGraphDst: Vector[String], - val hasORTriggerSemantics: Set[String] + val downample_repetitive_skips: Vector[Long], + val downample_initial_skips: Vector[Long], + val trigger_graph_src: Vector[String], + val trigger_graph_dst: Vector[String], + val has_or_trigger_semantics: Set[String] ) extends DecisionModel with CommunicatingExtendedDependenciesPeriodicWorkload with InstrumentedWorkloadMixin @@ -46,7 +46,7 @@ final case class CommunicatingAndTriggeredReactiveWorkload( for ((s, i) <- data_graph_src.zipWithIndex) yield (s, data_graph_dst(i), data_graph_message_size(i)) - lazy val triggerGraph = triggerGraphSrc.zip(triggerGraphDst) + lazy val triggerGraph = trigger_graph_src.zip(trigger_graph_dst) lazy val stimulusGraph = { val g = DefaultDirectedGraph[String, DefaultEdge](classOf[DefaultEdge]) @@ -72,7 +72,7 @@ final case class CommunicatingAndTriggeredReactiveWorkload( .map(stimulusGraph.getEdgeSource) .flatMap(pred => propagatedEvents.get(pred)) .foldLeft(Set[(Double, Double, Double)]())((s1, s2) => s1 | s2) - val events = if (periodic_sources.contains(next) || hasORTriggerSemantics.contains(next)) { + val events = if (periodic_sources.contains(next) || has_or_trigger_semantics.contains(next)) { incomingEvents } else { val maxP = incomingEvents.map((p, o, d) => p).max @@ -103,18 +103,18 @@ final case class CommunicatingAndTriggeredReactiveWorkload( val idxUpsample = upsamples.indexOf(next) propagatedEvents(next) = events.map(e => { ( - e._1 / upsampleRepetitiveHolds(idxUpsample).toDouble, + e._1 / upsample_repetitive_holds(idxUpsample).toDouble, e._2 + e._1, // / upsampleInitialHolds(idxUpsample).toDouble), - e._3 / upsampleRepetitiveHolds(idxUpsample).toDouble + e._3 / upsample_repetitive_holds(idxUpsample).toDouble ) }) } else if (downsamples.contains(next)) { val idxDownsample = downsamples.indexOf(next) propagatedEvents(next) = events.map(e => { ( - e._1 * downampleRepetitiveSkips(idxDownsample).toDouble, + e._1 * downample_repetitive_skips(idxDownsample).toDouble, e._2 + e._1, // * (downampleInitialSkips(idxDownsample).toDouble)), - e._3 * (downampleRepetitiveSkips(idxDownsample).toDouble) + e._3 * (downample_repetitive_skips(idxDownsample).toDouble) ) }) } @@ -144,7 +144,7 @@ final case class CommunicatingAndTriggeredReactiveWorkload( .getEdgeTarget(dst); if tasks.contains(dstTask) ) { - if (hasORTriggerSemantics.contains(dstTask)) { + if (has_or_trigger_semantics.contains(dstTask)) { for ( (srcEvent, i) <- processes.zipWithIndex .filter((p, i) => p == srcTask); @@ -173,20 +173,20 @@ final case class CommunicatingAndTriggeredReactiveWorkload( srcTask = stimulusGraph.getEdgeSource(src); dstTask = stimulusGraph.getEdgeTarget(dst); if tasks.contains(srcTask) && tasks.contains(dstTask) ) { - if (hasORTriggerSemantics.contains(dstTask)) { + if (has_or_trigger_semantics.contains(dstTask)) { for ( (srcEvent, i) <- processes.zipWithIndex .filter((p, i) => p == srcTask); (dstEvent, j) <- processes.zipWithIndex .filter((p, j) => p == dstTask) if periods(j) * Rational( - upsampleRepetitiveHolds(idxUpsample) + upsample_repetitive_holds(idxUpsample) ) == periods(i) && offsets(j) - (periods(j)) == offsets(i) ) { - affineControlGraphEdges :+= (i, j, upsampleRepetitiveHolds( + affineControlGraphEdges :+= (i, j, upsample_repetitive_holds( idxUpsample - ).toInt, upsampleInitialHolds(idxUpsample).toInt, 1, 0) + ).toInt, upsample_initial_holds(idxUpsample).toInt, 1, 0) } } else { for ( @@ -213,14 +213,14 @@ final case class CommunicatingAndTriggeredReactiveWorkload( srcTask = stimulusGraph.getEdgeSource(src); dstTask = stimulusGraph.getEdgeTarget(dst); if tasks.contains(srcTask) && tasks.contains(dstTask) ) { - if (hasORTriggerSemantics.contains(dstTask)) { + if (has_or_trigger_semantics.contains(dstTask)) { for ( (srcEvent, i) <- processes.zipWithIndex .filter((p, i) => p == srcTask); (dstEvent, j) <- processes.zipWithIndex .filter((p, j) => p == dstTask) if periods(j) / Rational( - downampleRepetitiveSkips(idxDownsample) + downample_repetitive_skips(idxDownsample) ) == periods(i) && offsets(j) + (periods(j) ) == offsets(i) ) @@ -229,8 +229,8 @@ final case class CommunicatingAndTriggeredReactiveWorkload( j, 1, 0, - downampleRepetitiveSkips(idxDownsample).toInt, - downampleInitialSkips(idxDownsample).toInt + downample_repetitive_skips(idxDownsample).toInt, + downample_initial_skips(idxDownsample).toInt ) } else { for ( diff --git a/scala-common/src/main/scala/idesyde/common/CommunicatingExtendedDependenciesPeriodicWorkload.scala b/scala-common/src/main/scala/idesyde/common/legacy/CommunicatingExtendedDependenciesPeriodicWorkload.scala similarity index 97% rename from scala-common/src/main/scala/idesyde/common/CommunicatingExtendedDependenciesPeriodicWorkload.scala rename to scala-common/src/main/scala/idesyde/common/legacy/CommunicatingExtendedDependenciesPeriodicWorkload.scala index a3c69712..8bdd41e5 100644 --- a/scala-common/src/main/scala/idesyde/common/CommunicatingExtendedDependenciesPeriodicWorkload.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/CommunicatingExtendedDependenciesPeriodicWorkload.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import spire.math.Rational // import scalax.collection.Graph diff --git a/scala-common/src/main/scala/idesyde/common/InstrumentedComputationTimes.scala b/scala-common/src/main/scala/idesyde/common/legacy/InstrumentedComputationTimes.scala similarity index 94% rename from scala-common/src/main/scala/idesyde/common/InstrumentedComputationTimes.scala rename to scala-common/src/main/scala/idesyde/common/legacy/InstrumentedComputationTimes.scala index ba90e772..4d30d4a5 100644 --- a/scala-common/src/main/scala/idesyde/common/InstrumentedComputationTimes.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/InstrumentedComputationTimes.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/InstrumentedPlatformMixin.scala b/scala-common/src/main/scala/idesyde/common/legacy/InstrumentedPlatformMixin.scala similarity index 80% rename from scala-common/src/main/scala/idesyde/common/InstrumentedPlatformMixin.scala rename to scala-common/src/main/scala/idesyde/common/legacy/InstrumentedPlatformMixin.scala index 577a7974..c3c181ec 100644 --- a/scala-common/src/main/scala/idesyde/common/InstrumentedPlatformMixin.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/InstrumentedPlatformMixin.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy trait InstrumentedPlatformMixin[RealT] { diff --git a/scala-common/src/main/scala/idesyde/common/InstrumentedWorkloadMixin.scala b/scala-common/src/main/scala/idesyde/common/legacy/InstrumentedWorkloadMixin.scala similarity index 81% rename from scala-common/src/main/scala/idesyde/common/InstrumentedWorkloadMixin.scala rename to scala-common/src/main/scala/idesyde/common/legacy/InstrumentedWorkloadMixin.scala index 8d434588..cba962c5 100644 --- a/scala-common/src/main/scala/idesyde/common/InstrumentedWorkloadMixin.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/InstrumentedWorkloadMixin.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy trait InstrumentedWorkloadMixin { diff --git a/scala-common/src/main/scala/idesyde/common/legacy/MixedRules.scala b/scala-common/src/main/scala/idesyde/common/legacy/MixedRules.scala new file mode 100644 index 00000000..3cc51b3b --- /dev/null +++ b/scala-common/src/main/scala/idesyde/common/legacy/MixedRules.scala @@ -0,0 +1,191 @@ +package idesyde.common.legacy + +import idesyde.core.DecisionModel +import idesyde.core.DesignModel +import scala.collection.mutable +import idesyde.common.legacy.CommonModule.tryCast + +trait MixedRules { + + def identTaskdAndSDFServer( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[PeriodicWorkloadAndSDFServers], Set[String]) = { + var errors = mutable.Set[String]() + tryCast(identified, classOf[SDFApplicationWithFunctions]) { sdfDecisionModels => + for (a <- sdfDecisionModels) { + if (!a.isConsistent) { + errors += s"identTaskdAndSDFServer: SDFApplication containing ${a.actorsIdentifiers.head} is inconsistent. Ignoring it." + } + } + tryCast(identified, classOf[CommunicatingAndTriggeredReactiveWorkload]) { taskDecisionModels => + ( + sdfDecisionModels + .filter(_.isConsistent) + .flatMap(a => + taskDecisionModels.map(b => + PeriodicWorkloadAndSDFServers( + sdfApplications = a, + workload = b + ) + ) + ), + errors.toSet + ) + } + } + } + + def identSDFToTiledMultiCore( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[SDFToTiledMultiCore], Set[String]) = { + var errors = mutable.Set[String]() + tryCast(identified, classOf[SDFApplicationWithFunctions]) { apps => + if (apps.isEmpty) { + errors += "identSDFToTiledMultiCore: no SDFApplicationWithFunctions found" + } + for (a <- apps) { + if (!a.isConsistent) { + errors += s"identSDFToTiledMultiCore: SDFApplication containing ${a.actorsIdentifiers.head} is inconsistent. Ignoring it." + } + } + tryCast(identified, classOf[SchedulableTiledMultiCore]) { plats => + if (plats.isEmpty) { + errors += "identSDFToTiledMultiCore: no SchedulableTiledMultiCore found" + } + ( + apps + .filter(_.isConsistent) + .flatMap(a => + plats.map(p => + SDFToTiledMultiCore( + sdfApplications = a, + platform = p, + processMappings = Vector.empty, + messageMappings = Vector.empty, + schedulerSchedules = Vector.empty, + messageSlotAllocations = Vector.empty, + actorThroughputs = Vector.empty + ) + ) + ), + errors.toSet + ) + } + } + } + + def identSDFToPartitionedSharedMemory( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[SDFToPartitionedSharedMemory], Set[String]) = { + var errors = mutable.Set[String]() + tryCast(identified, classOf[SDFApplicationWithFunctions]) { apps => + if (apps.isEmpty) { + errors += "identSDFToPartitionedSharedMemory: no SDFApplicationWithFunctions found" + } + for (a <- apps) { + if (!a.isConsistent) { + errors += s"identSDFToPartitionedSharedMemory: SDFApplication containing ${a.actorsIdentifiers.head} is inconsistent. Ignoring it." + } + } + tryCast(identified, classOf[PartitionedSharedMemoryMultiCore]) { plats => + if (plats.isEmpty) { + errors += "identSDFToPartitionedSharedMemory: no PartitionedSharedMemoryMultiCore found" + } + ( + apps + .filter(_.isConsistent) + .flatMap(a => + plats.map(p => + SDFToPartitionedSharedMemory( + sdfApplications = a, + platform = p, + processMappings = Vector.empty, + memoryMappings = Vector.empty, + messageSlotAllocations = Vector.empty + ) + ) + ), + errors.toSet + ) + } + } + } + + def identPeriodicWorkloadToPartitionedSharedMultiCore( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[PeriodicWorkloadToPartitionedSharedMultiCore], Set[String]) = { + tryCast(identified, classOf[CommunicatingAndTriggeredReactiveWorkload]) { apps => + tryCast(identified, classOf[PartitionedSharedMemoryMultiCore]) { plats => + val (m, e) = apps + .flatMap(a => + plats + .map(p => + val potential = PeriodicWorkloadToPartitionedSharedMultiCore( + workload = a, + platform = p, + processMappings = Vector.empty, + processSchedulings = Vector.empty, + channelMappings = Vector.empty, + channelSlotAllocations = Map(), + maxUtilizations = Map() + ) + if ( + potential.wcets.zipWithIndex + .forall((wi, i) => wi.exists(w => w > 0.0 && w <= a.relative_deadlines(i))) + ) { + (Some(potential), None) + } else { + ( + None, + Some( + "identPeriodicWorkloadToPartitionedSharedMultiCore: not all tasks are mappable to the platform" + ) + ) + } + ) + ) + .unzip + (m.flatten, e.flatten) + } + } + // val app = identified + // .filter(_.isInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) + // .map(_.asInstanceOf[CommunicatingAndTriggeredReactiveWorkload]) + // val plat = identified + // .filter(_.isInstanceOf[PartitionedSharedMemoryMultiCore]) + // .map(_.asInstanceOf[PartitionedSharedMemoryMultiCore]) + // if ((runtimes.isDefined && plat.isEmpty) || (runtimes.isEmpty && plat.isDefined)) + } + + def identTaksAndSDFServerToMultiCore( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[PeriodicWorkloadAndSDFServerToMultiCoreOld], Set[String]) = { + tryCast(identified, classOf[PeriodicWorkloadAndSDFServers]) {apps => + tryCast(identified, classOf[PartitionedSharedMemoryMultiCore]) {plats => + ( + apps.flatMap(a => + plats.map(p => + PeriodicWorkloadAndSDFServerToMultiCoreOld( + tasksAndSDFs = a, + platform = p, + processesSchedulings = Vector.empty, + processesMappings = Vector.empty, + messagesMappings = Vector.empty, + messageSlotAllocations = Map.empty, + sdfServerUtilization = Vector.empty[Double], + sdfOrderBasedSchedules = p.runtimes.schedulers.map(p => Vector.empty) + ) + ) + ), + Set() + ) + } + } + } + +} diff --git a/scala-common/src/main/scala/idesyde/common/ParametricRateDataflowWorkloadMixin.scala b/scala-common/src/main/scala/idesyde/common/legacy/ParametricRateDataflowWorkloadMixin.scala similarity index 97% rename from scala-common/src/main/scala/idesyde/common/ParametricRateDataflowWorkloadMixin.scala rename to scala-common/src/main/scala/idesyde/common/legacy/ParametricRateDataflowWorkloadMixin.scala index b7f0a6aa..78b0ca02 100644 --- a/scala-common/src/main/scala/idesyde/common/ParametricRateDataflowWorkloadMixin.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/ParametricRateDataflowWorkloadMixin.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters.* import scala.jdk.StreamConverters.* diff --git a/scala-common/src/main/scala/idesyde/common/PartitionedCoresWithRuntimes.scala b/scala-common/src/main/scala/idesyde/common/legacy/PartitionedCoresWithRuntimes.scala similarity index 81% rename from scala-common/src/main/scala/idesyde/common/PartitionedCoresWithRuntimes.scala rename to scala-common/src/main/scala/idesyde/common/legacy/PartitionedCoresWithRuntimes.scala index 3bd90a31..eddeda55 100644 --- a/scala-common/src/main/scala/idesyde/common/PartitionedCoresWithRuntimes.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/PartitionedCoresWithRuntimes.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ @@ -10,9 +10,9 @@ import java.{util => ju} final case class PartitionedCoresWithRuntimes( val processors: Vector[String], val schedulers: Vector[String], - val isBareMetal: Vector[Boolean], - val isFixedPriority: Vector[Boolean], - val isCyclicExecutive: Vector[Boolean] + val is_bare_metal: Vector[Boolean], + val is_fixed_priority: Vector[Boolean], + val is_cyclic_executive: Vector[Boolean] ) extends DecisionModel derives ReadWriter { diff --git a/scala-common/src/main/scala/idesyde/common/PartitionedSharedMemoryMultiCore.scala b/scala-common/src/main/scala/idesyde/common/legacy/PartitionedSharedMemoryMultiCore.scala similarity index 93% rename from scala-common/src/main/scala/idesyde/common/PartitionedSharedMemoryMultiCore.scala rename to scala-common/src/main/scala/idesyde/common/legacy/PartitionedSharedMemoryMultiCore.scala index 99565f0e..5d4a154d 100644 --- a/scala-common/src/main/scala/idesyde/common/PartitionedSharedMemoryMultiCore.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/PartitionedSharedMemoryMultiCore.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/PeriodicWorkloadAndSDFServers.scala b/scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadAndSDFServers.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/PeriodicWorkloadAndSDFServers.scala rename to scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadAndSDFServers.scala index 165c8f8a..68bdb2d6 100644 --- a/scala-common/src/main/scala/idesyde/common/PeriodicWorkloadAndSDFServers.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadAndSDFServers.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/PeriodicWorkloadAndSDFServersToMultiCoreOld.scala b/scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadAndSDFServersToMultiCoreOld.scala similarity index 96% rename from scala-common/src/main/scala/idesyde/common/PeriodicWorkloadAndSDFServersToMultiCoreOld.scala rename to scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadAndSDFServersToMultiCoreOld.scala index 55107e9d..b5bb1734 100644 --- a/scala-common/src/main/scala/idesyde/common/PeriodicWorkloadAndSDFServersToMultiCoreOld.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadAndSDFServersToMultiCoreOld.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/PeriodicWorkloadToPartitionedSharedMultiCore.scala b/scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadToPartitionedSharedMultiCore.scala similarity index 96% rename from scala-common/src/main/scala/idesyde/common/PeriodicWorkloadToPartitionedSharedMultiCore.scala rename to scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadToPartitionedSharedMultiCore.scala index 07836b6f..9a2bed5f 100644 --- a/scala-common/src/main/scala/idesyde/common/PeriodicWorkloadToPartitionedSharedMultiCore.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/PeriodicWorkloadToPartitionedSharedMultiCore.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/legacy/PlatformRules.scala b/scala-common/src/main/scala/idesyde/common/legacy/PlatformRules.scala new file mode 100644 index 00000000..79b9f70a --- /dev/null +++ b/scala-common/src/main/scala/idesyde/common/legacy/PlatformRules.scala @@ -0,0 +1,123 @@ +package idesyde.common.legacy + +import scala.jdk.CollectionConverters._ + +import idesyde.core.DesignModel +import idesyde.core.DecisionModel +import scala.collection.mutable +import org.jgrapht.alg.shortestpath.FloydWarshallShortestPaths +import idesyde.common.legacy.CommonModule.tryCast + +trait PlatformRules { + + def identSchedulableTiledMultiCore( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[SchedulableTiledMultiCore], Set[String]) = { + tryCast(identified, classOf[PartitionedCoresWithRuntimes]) { runtimes => + tryCast(identified, classOf[TiledMultiCoreWithFunctions]) { plats => + ( + runtimes.flatMap(r => + plats.map(p => SchedulableTiledMultiCore(hardware = p, runtimes = r)) + ), + Set() + ) + } + } + } + + def identPartitionedSharedMemoryMultiCore( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[PartitionedSharedMemoryMultiCore], Set[String]) = { + tryCast(identified, classOf[PartitionedCoresWithRuntimes]) { runtimes => + tryCast(identified, classOf[SharedMemoryMultiCore]) { plats => + ( + runtimes.flatMap(r => + plats.map(p => PartitionedSharedMemoryMultiCore(hardware = p, runtimes = r)) + ), + Set() + ) + } + } + } + + def identTiledFromShared( + models: Set[DesignModel], + identified: Set[DecisionModel] + ): (Set[TiledMultiCoreWithFunctions], Set[String]) = { + tryCast(identified, classOf[SharedMemoryMultiCore]) { plats => + var tiledPlats = mutable.Set[TiledMultiCoreWithFunctions]() + var errors = mutable.Set[String]() + for (plat <- plats) { + val isTiled = plat.communicationElems.forall(p => + plat.topology + .outgoingEdgesOf(p) + .asScala + .map(plat.topology.getEdgeTarget) + .count(e => plat.storageElems.contains(e) || plat.processingElems.contains(e)) <= 2 + ) && + plat.storageElems.forall(p => + plat.topology + .outgoingEdgesOf(p) + .asScala + .map(plat.topology.getEdgeTarget) + .count(e => plat.communicationElems.contains(e)) <= 1 + ) && + plat.processingElems.length == plat.storageElems.length + if (isTiled) { + val shortestPaths = FloydWarshallShortestPaths(plat.topology) + val tiledMemories = plat.processingElems.map(pe => + plat.storageElems.minBy(me => + // plat.topology.get(pe).shortestPathTo(plat.topology.get(me)) match { + // case Some(path) => path.size + // case None => plat.communicationElems.length + 1 + // } + val path = shortestPaths.getPath(pe, me) + if (path != null) { + path.getLength() + } else { + plat.communicationElems.length + 1 + } + ) + ) + val tiledNI = plat.processingElems.map(pe => + plat.communicationElems.minBy(ce => + // plat.topology.get(pe).shortestPathTo(plat.topology.get(ce)) match { + // case Some(value) => value.size + // case None => plat.topology.nodes.size + // } + val path = shortestPaths.getPath(pe, ce) + if (path != null) { + path.getLength() + } else { + plat.communicationElems.length + 1 + } + ) + ) + val routers = plat.communicationElems.filterNot(tiledNI.contains) + tiledPlats += TiledMultiCoreWithFunctions( + processors = plat.processingElems, + memories = tiledMemories, + networkInterfaces = tiledNI, + routers = routers, + interconnectTopologySrcs = plat.topologySrcs, + interconnectTopologyDsts = plat.topologyDsts, + processorsProvisions = plat.processorsProvisions, + processorsFrequency = plat.processorsFrequency, + tileMemorySizes = + tiledMemories.map(me => plat.storageSizes(plat.storageElems.indexOf(me))), + communicationElementsMaxChannels = plat.communicationElementsMaxChannels, + communicationElementsBitPerSecPerChannel = + plat.communicationElementsBitPerSecPerChannel, + preComputedPaths = plat.preComputedPaths + ) + } else { + errors += s"identTiledFromShared: The shared memory platform containing processing element ${plat.processingElems.head} is not tiled." + } + } + (tiledPlats.toSet, errors.toSet) + } + } + +} diff --git a/scala-common/src/main/scala/idesyde/common/RuntimesAndProcessors.scala b/scala-common/src/main/scala/idesyde/common/legacy/RuntimesAndProcessors.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/RuntimesAndProcessors.scala rename to scala-common/src/main/scala/idesyde/common/legacy/RuntimesAndProcessors.scala index c5524f42..bf1739aa 100644 --- a/scala-common/src/main/scala/idesyde/common/RuntimesAndProcessors.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/RuntimesAndProcessors.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/SDFApplication.scala b/scala-common/src/main/scala/idesyde/common/legacy/SDFApplication.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/SDFApplication.scala rename to scala-common/src/main/scala/idesyde/common/legacy/SDFApplication.scala index 92f329e6..4268d187 100644 --- a/scala-common/src/main/scala/idesyde/common/SDFApplication.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/SDFApplication.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ diff --git a/scala-common/src/main/scala/idesyde/common/SDFApplicationWithFunctions.scala b/scala-common/src/main/scala/idesyde/common/legacy/SDFApplicationWithFunctions.scala similarity index 97% rename from scala-common/src/main/scala/idesyde/common/SDFApplicationWithFunctions.scala rename to scala-common/src/main/scala/idesyde/common/legacy/SDFApplicationWithFunctions.scala index b9b2c678..074c2463 100644 --- a/scala-common/src/main/scala/idesyde/common/SDFApplicationWithFunctions.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/SDFApplicationWithFunctions.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters.* diff --git a/scala-common/src/main/scala/idesyde/common/SDFToPartitionedSharedMemory.scala b/scala-common/src/main/scala/idesyde/common/legacy/SDFToPartitionedSharedMemory.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/SDFToPartitionedSharedMemory.scala rename to scala-common/src/main/scala/idesyde/common/legacy/SDFToPartitionedSharedMemory.scala index 58889709..090dc3c6 100644 --- a/scala-common/src/main/scala/idesyde/common/SDFToPartitionedSharedMemory.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/SDFToPartitionedSharedMemory.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters.* diff --git a/scala-common/src/main/scala/idesyde/common/SDFToTiledMultiCore.scala b/scala-common/src/main/scala/idesyde/common/legacy/SDFToTiledMultiCore.scala similarity index 96% rename from scala-common/src/main/scala/idesyde/common/SDFToTiledMultiCore.scala rename to scala-common/src/main/scala/idesyde/common/legacy/SDFToTiledMultiCore.scala index f32d7f21..5808418f 100644 --- a/scala-common/src/main/scala/idesyde/common/SDFToTiledMultiCore.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/SDFToTiledMultiCore.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters.* diff --git a/scala-common/src/main/scala/idesyde/common/SchedulableTiledMultiCore.scala b/scala-common/src/main/scala/idesyde/common/legacy/SchedulableTiledMultiCore.scala similarity index 94% rename from scala-common/src/main/scala/idesyde/common/SchedulableTiledMultiCore.scala rename to scala-common/src/main/scala/idesyde/common/legacy/SchedulableTiledMultiCore.scala index 484ba256..627321c7 100644 --- a/scala-common/src/main/scala/idesyde/common/SchedulableTiledMultiCore.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/SchedulableTiledMultiCore.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ import upickle.default.* diff --git a/scala-common/src/main/scala/idesyde/common/SharedMemoryMultiCore.scala b/scala-common/src/main/scala/idesyde/common/legacy/SharedMemoryMultiCore.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/SharedMemoryMultiCore.scala rename to scala-common/src/main/scala/idesyde/common/legacy/SharedMemoryMultiCore.scala index 7b206221..544aebfb 100644 --- a/scala-common/src/main/scala/idesyde/common/SharedMemoryMultiCore.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/SharedMemoryMultiCore.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.OptionConverters.* import scala.jdk.CollectionConverters.* @@ -6,7 +6,7 @@ import scala.jdk.StreamConverters.* import spire.math.Rational import spire.implicits.* import idesyde.core.DecisionModel -import idesyde.common.InstrumentedPlatformMixin +import idesyde.common.legacy.InstrumentedPlatformMixin import idesyde.core.DecisionModel import upickle.default._ import upickle.implicits.key diff --git a/scala-common/src/main/scala/idesyde/common/StandardDecisionModel.scala b/scala-common/src/main/scala/idesyde/common/legacy/StandardDecisionModel.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/StandardDecisionModel.scala rename to scala-common/src/main/scala/idesyde/common/legacy/StandardDecisionModel.scala index 9fd8645a..74fa8bb1 100644 --- a/scala-common/src/main/scala/idesyde/common/StandardDecisionModel.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/StandardDecisionModel.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import idesyde.core.DecisionModel diff --git a/scala-common/src/main/scala/idesyde/common/TiledMultiCoreWithFunctions.scala b/scala-common/src/main/scala/idesyde/common/legacy/TiledMultiCoreWithFunctions.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/TiledMultiCoreWithFunctions.scala rename to scala-common/src/main/scala/idesyde/common/legacy/TiledMultiCoreWithFunctions.scala index 969df097..b2d751ac 100644 --- a/scala-common/src/main/scala/idesyde/common/TiledMultiCoreWithFunctions.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/TiledMultiCoreWithFunctions.scala @@ -1,10 +1,10 @@ -package idesyde.common +package idesyde.common.legacy import scala.jdk.CollectionConverters._ import upickle.default.* -import idesyde.common.InstrumentedPlatformMixin +import idesyde.common.legacy.InstrumentedPlatformMixin import idesyde.core.DecisionModel import java.{util => ju} import org.jgrapht.graph.DefaultDirectedGraph diff --git a/scala-common/src/main/scala/idesyde/common/WCETComputationMixin.scala b/scala-common/src/main/scala/idesyde/common/legacy/WCETComputationMixin.scala similarity index 95% rename from scala-common/src/main/scala/idesyde/common/WCETComputationMixin.scala rename to scala-common/src/main/scala/idesyde/common/legacy/WCETComputationMixin.scala index f9aaf569..50a35c80 100644 --- a/scala-common/src/main/scala/idesyde/common/WCETComputationMixin.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/WCETComputationMixin.scala @@ -1,4 +1,4 @@ -package idesyde.common +package idesyde.common.legacy import scala.reflect.ClassTag import spire._ diff --git a/scala-common/src/main/scala/idesyde/common/WorkloadRules.scala b/scala-common/src/main/scala/idesyde/common/legacy/WorkloadRules.scala similarity index 60% rename from scala-common/src/main/scala/idesyde/common/WorkloadRules.scala rename to scala-common/src/main/scala/idesyde/common/legacy/WorkloadRules.scala index 53a5293e..a4a1163a 100644 --- a/scala-common/src/main/scala/idesyde/common/WorkloadRules.scala +++ b/scala-common/src/main/scala/idesyde/common/legacy/WorkloadRules.scala @@ -1,7 +1,8 @@ -package idesyde.common +package idesyde.common.legacy import idesyde.core.DesignModel import idesyde.core.DecisionModel +import idesyde.common.legacy.CommonModule.tryCast trait WorkloadRules { @@ -9,13 +10,8 @@ trait WorkloadRules { models: Set[DesignModel], identified: Set[DecisionModel] ): (Set[CommunicatingAndTriggeredReactiveWorkload], Set[String]) = - ( - identified - .flatMap(_ match { - case m: CommunicatingAndTriggeredReactiveWorkload => Some(m) - case _ => None - }) - .reduceOption((m1, m2) => { + tryCast(identified, classOf[CommunicatingAndTriggeredReactiveWorkload]) { filtered => + val proper = filtered.reduceOption((m1, m2) => { CommunicatingAndTriggeredReactiveWorkload( tasks = m1.tasks ++ m2.tasks, task_sizes = m1.task_sizes ++ m2.task_sizes, @@ -31,19 +27,19 @@ trait WorkloadRules { offsets_numerator = m1.offsets_numerator ++ m2.offsets_numerator, offsets_denominator = m1.offsets_denominator ++ m2.offsets_denominator, upsamples = m1.upsamples ++ m2.upsamples, - upsampleRepetitiveHolds = m1.upsampleRepetitiveHolds ++ m2.upsampleRepetitiveHolds, - upsampleInitialHolds = m1.upsampleInitialHolds ++ m2.upsampleInitialHolds, + upsample_repetitive_holds = m1.upsample_repetitive_holds ++ m2.upsample_repetitive_holds, + upsample_initial_holds = m1.upsample_initial_holds ++ m2.upsample_initial_holds, downsamples = m1.downsamples ++ m2.downsamples, - downampleRepetitiveSkips = m1.downampleRepetitiveSkips ++ m2.downampleRepetitiveSkips, - downampleInitialSkips = m1.downampleInitialSkips ++ m2.downampleInitialSkips, - triggerGraphSrc = m1.triggerGraphSrc ++ m2.triggerGraphSrc, - triggerGraphDst = m1.triggerGraphDst ++ m2.triggerGraphDst, - hasORTriggerSemantics = m1.hasORTriggerSemantics ++ m2.hasORTriggerSemantics + downample_repetitive_skips = m1.downample_repetitive_skips ++ m2.downample_repetitive_skips, + downample_initial_skips = m1.downample_initial_skips ++ m2.downample_initial_skips, + trigger_graph_src = m1.trigger_graph_src ++ m2.trigger_graph_src, + trigger_graph_dst = m1.trigger_graph_dst ++ m2.trigger_graph_dst, + has_or_trigger_semantics = m1.has_or_trigger_semantics ++ m2.has_or_trigger_semantics ) }) .map(Set(_)) - .getOrElse(Set()), - Set() - ) + .getOrElse(Set()) + (proper, Set()) + } } diff --git a/settings.gradle b/settings.gradle index 98422b03..fa53fba3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ include 'java-core' +include 'java-core-generator' //include 'java-bridge-matlab' include 'java-common' include 'java-blueprints'