From a0ced8112395dacd011ce141fe0a11962cf3282a Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Mon, 8 Jun 2026 09:44:03 +0200 Subject: [PATCH] feat(minerva): add runnable sample task Refs #687 --- docs/export/minerva.md | 8 +++- .../ROOT/pages/how-to/minerva-export.adoc | 15 ++++++- .../skainet-compile-minerva/build.gradle.kts | 43 +++++++++++++++++++ .../examples/MinervaTinyMlpExportSample.kt | 12 ++++-- .../MinervaTinyMlpExportSampleTest.kt | 3 +- 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/docs/export/minerva.md b/docs/export/minerva.md index ca467d74..7d8a6ef6 100644 --- a/docs/export/minerva.md +++ b/docs/export/minerva.md @@ -114,7 +114,13 @@ The generated firmware example intentionally contains placeholders. Confirm the ## Maintained JVM Sample -`sk.ainet.compile.minerva.examples.MinervaTinyMlpExportSample` builds a tiny two-layer MLP, reads Minerva paths from environment variables, invokes the export facade, and prints bundle and verification status. `MinervaTinyMlpExportSampleTest` validates the sample graph and NPZ generation without real device keys. +`sk.ainet.compile.minerva.examples.MinervaTinyMlpExportSample` builds a tiny two-layer MLP, reads Minerva paths from environment variables or Gradle properties, invokes the export facade, and prints bundle and verification status. + +```bash +./gradlew :skainet-compile:skainet-compile-minerva:runMinervaTinyMlpSample +``` + +Without `MINERVA_COMPILER_SCRIPT`, the task runs a dry validation through compatibility, lowering, and in-memory NPZ generation. Add `-Pminerva.compilerScript`, `-Pminerva.runtimeRoot`, `-Pminerva.calibrationNpz`, and `-Pminerva.keyFile` to run the real compiler path. `MinervaTinyMlpExportSampleTest` validates the sample graph and NPZ generation without real device keys. ## Export Path Choice diff --git a/docs/modules/ROOT/pages/how-to/minerva-export.adoc b/docs/modules/ROOT/pages/how-to/minerva-export.adoc index 31455636..0b25f05e 100644 --- a/docs/modules/ROOT/pages/how-to/minerva-export.adoc +++ b/docs/modules/ROOT/pages/how-to/minerva-export.adoc @@ -211,7 +211,20 @@ Run the sample after configuring libminerva: [source,bash] ---- ./gradlew :skainet-compile:skainet-compile-minerva:jvmTest -./gradlew :skainet-compile:skainet-compile-minerva:jvmJar +./gradlew :skainet-compile:skainet-compile-minerva:runMinervaTinyMlpSample +---- + +Without `MINERVA_COMPILER_SCRIPT`, the sample task runs a dry validation through compatibility, lowering, and in-memory NPZ generation. Configure the runtime with Gradle properties or matching environment variables to run the real compiler and host verification: + +[source,bash] +---- +./gradlew :skainet-compile:skainet-compile-minerva:runMinervaTinyMlpSample \ + -Pminerva.compilerScript="$MINERVA_COMPILER_SCRIPT" \ + -Pminerva.runtimeRoot="$MINERVA_RUNTIME_ROOT" \ + -Pminerva.calibrationNpz="$MINERVA_CALIBRATION_NPZ" \ + -Pminerva.keyFile="$MINERVA_KEY_FILE" \ + -Pminerva.hostVerification.runCmakeBuild=true \ + -Pminerva.hostVerification.runCTest=true ---- The sample graph is covered by `MinervaTinyMlpExportSampleTest`, which validates compatibility, lowering, and NPZ generation without requiring real device keys. diff --git a/skainet-compile/skainet-compile-minerva/build.gradle.kts b/skainet-compile/skainet-compile-minerva/build.gradle.kts index bf41b2fe..e5a17305 100644 --- a/skainet-compile/skainet-compile-minerva/build.gradle.kts +++ b/skainet-compile/skainet-compile-minerva/build.gradle.kts @@ -34,6 +34,13 @@ val minervaHostVerificationEnabled = providers.gradleProperty("minerva.hostVerif .orElse(false) val minervaRuntimeRoot = providers.gradleProperty("minerva.runtimeRoot") val minervaCompilerScript = providers.gradleProperty("minerva.compilerScript") +val minervaKeyFile = providers.gradleProperty("minerva.keyFile") +val minervaCalibrationNpz = providers.gradleProperty("minerva.calibrationNpz") +val minervaRunCmakeBuild = providers.gradleProperty("minerva.hostVerification.runCmakeBuild") +val minervaRunCTest = providers.gradleProperty("minerva.hostVerification.runCTest") +val minervaHostOutputPath = providers.gradleProperty("minerva.hostVerification.hostOutputPath") + +val jvmMainCompilation = kotlin.targets.getByName("jvm").compilations.getByName("main") tasks.register("minervaHostVerification") { group = "verification" @@ -47,3 +54,39 @@ tasks.register("minervaHostVerification") { inputs.property("minerva.runtimeRoot", minervaRuntimeRoot.orElse("")) inputs.property("minerva.compilerScript", minervaCompilerScript.orElse("")) } + +tasks.register("runMinervaTinyMlpSample") { + group = "application" + description = "Runs the maintained Minerva tiny MLP export sample." + + dependsOn(tasks.named("jvmJar")) + + mainClass.set("sk.ainet.compile.minerva.examples.MinervaTinyMlpExportSample") + + classpath = files( + jvmMainCompilation.runtimeDependencyFiles, + tasks.named("jvmJar").get().outputs.files + ) + + minervaCompilerScript.orElse(providers.environmentVariable("MINERVA_COMPILER_SCRIPT")).orNull?.let { + environment("MINERVA_COMPILER_SCRIPT", it) + } + minervaRuntimeRoot.orElse(providers.environmentVariable("MINERVA_RUNTIME_ROOT")).orNull?.let { + environment("MINERVA_RUNTIME_ROOT", it) + } + minervaKeyFile.orElse(providers.environmentVariable("MINERVA_KEY_FILE")).orNull?.let { + environment("MINERVA_KEY_FILE", it) + } + minervaCalibrationNpz.orElse(providers.environmentVariable("MINERVA_CALIBRATION_NPZ")).orNull?.let { + environment("MINERVA_CALIBRATION_NPZ", it) + } + minervaRunCmakeBuild.orElse(providers.environmentVariable("MINERVA_RUN_CMAKE")).orNull?.let { + environment("MINERVA_RUN_CMAKE", it) + } + minervaRunCTest.orElse(providers.environmentVariable("MINERVA_RUN_CTEST")).orNull?.let { + environment("MINERVA_RUN_CTEST", it) + } + minervaHostOutputPath.orElse(providers.environmentVariable("MINERVA_HOST_OUTPUT_PATH")).orNull?.let { + environment("MINERVA_HOST_OUTPUT_PATH", it) + } +} diff --git a/skainet-compile/skainet-compile-minerva/src/jvmMain/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSample.kt b/skainet-compile/skainet-compile-minerva/src/jvmMain/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSample.kt index 40adf456..c62a55df 100644 --- a/skainet-compile/skainet-compile-minerva/src/jvmMain/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSample.kt +++ b/skainet-compile/skainet-compile-minerva/src/jvmMain/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSample.kt @@ -1,6 +1,7 @@ package sk.ainet.compile.minerva.examples import sk.ainet.compile.minerva.MinervaExportFacade +import sk.ainet.compile.minerva.MinervaExportFailureKind import sk.ainet.compile.minerva.MinervaExportOptions import sk.ainet.compile.minerva.MinervaHostVerificationMetadata import sk.ainet.lang.graph.DefaultComputeGraph @@ -20,12 +21,11 @@ import sk.ainet.lang.types.DType internal object MinervaTinyMlpExportSample { @JvmStatic - internal fun main(args: Array): Unit { + public fun main(args: Array): Unit { val env = System.getenv() val compilerScript = envPath(env, "MINERVA_COMPILER_SCRIPT") if (compilerScript == null) { - println("Set MINERVA_COMPILER_SCRIPT to run the Minerva tiny MLP export sample.") - return + println("MINERVA_COMPILER_SCRIPT is not set; running dry validation through NPZ generation.") } val options = exportOptions( @@ -47,6 +47,10 @@ internal object MinervaTinyMlpExportSample { result.hostVerification?.let { verification -> println("Host verification: ${verification.status}") } + if (compilerScript == null && result.failure?.kind == MinervaExportFailureKind.COMPILER_PREREQUISITE_FAILED) { + println("Dry validation completed: graph is compatible and model.npz was generated in memory.") + return + } if (result.failed) { error(result.failure?.message ?: "Minerva export failed.") } @@ -114,7 +118,7 @@ internal object MinervaTinyMlpExportSample { internal fun exportOptions( outputDir: String = "build/minerva", projectName: String = "TinySecureMlp", - compilerScript: String, + compilerScript: String? = null, runtimeRoot: String? = null, keyFile: String? = null, calibrationNpz: String? = null, diff --git a/skainet-compile/skainet-compile-minerva/src/jvmTest/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSampleTest.kt b/skainet-compile/skainet-compile-minerva/src/jvmTest/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSampleTest.kt index cdeafaea..1c058444 100644 --- a/skainet-compile/skainet-compile-minerva/src/jvmTest/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSampleTest.kt +++ b/skainet-compile/skainet-compile-minerva/src/jvmTest/kotlin/sk/ainet/compile/minerva/examples/MinervaTinyMlpExportSampleTest.kt @@ -27,7 +27,8 @@ class MinervaTinyMlpExportSampleTest { assertTrue(report.compatible, report.issues.joinToString { it.message }) assertEquals(2, report.layerCount) - val result = MinervaExportFacade().exportGraph(graph, options.copy(compilerScript = null)) + val dryRunOptions = MinervaTinyMlpExportSample.exportOptions() + val result = MinervaExportFacade().exportGraph(graph, dryRunOptions) assertEquals(GraphExportStatus.FAILED, result.status) assertEquals(MinervaExportFailureKind.COMPILER_PREREQUISITE_FAILED, result.failure?.kind)