diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 7899d82ce..4b167c878 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -14,6 +14,7 @@ ** xref:how-to/arduino-c-codegen.adoc[Generate C for Arduino] * Reference ** xref:reference/architecture.adoc[Architecture] +** xref:reference/graph-export-architecture.adoc[Graph export architecture] ** xref:reference/operators/generated/index.adoc[Operator reference] ** xref:reference/ops-status-matrix.adoc[Operator coverage matrix] ** xref:reference/api.adoc[API reference (Dokka)] diff --git a/docs/modules/ROOT/pages/reference/graph-export-architecture.adoc b/docs/modules/ROOT/pages/reference/graph-export-architecture.adoc new file mode 100644 index 000000000..a7da2a0a6 --- /dev/null +++ b/docs/modules/ROOT/pages/reference/graph-export-architecture.adoc @@ -0,0 +1,100 @@ += Graph Export Architecture + +SKaiNET export backends should share the same graph-export workflow while keeping backend writers separate. + +The shared boundary is the captured graph representation. The shared contracts live in `skainet-compile-core` under `sk.ainet.compile.export` and cover diagnostics, generated artifact metadata, result envelopes, mutable export contexts, writers, verifiers, and component naming conventions. + +== Shared workflow + +[source,text] +---- +source model or imported model + -> graph capture + -> validation + -> backend lowering + -> backend writer + -> optional packaging + -> optional verification +---- + +Backends can skip stages that do not apply. For example, StableHLO does not need a project packager, while Minerva does. + +== Backend writers + +StableHLO and Minerva should not be forced into one writer abstraction beyond the common result and diagnostic model. + +[cols="1,2,2", options="header"] +|=== +| Backend +| Lowering model +| Writer output + +| StableHLO +| Individual graph operations are lowered through operation converters. +| MLIR module text represented by `StableHloModule`. + +| Minerva +| Supported graph patterns are lowered into sequential layer descriptors. +| Minerva intermediate model, `.npz` compiler input, generated C artifacts, packaged host and firmware project. +|=== + +== Naming conventions + +Use the following suffixes consistently for export components: + +[cols="1,2", options="header"] +|=== +| Suffix +| Responsibility + +| `Converter` +| Lower source graph structures into a backend intermediate. + +| `Context` +| Carry backend state, diagnostics, and generated artifacts. + +| `Registry` +| Map operation names or graph patterns to backend converters. + +| `Factory` +| Construct a backend exporter with standard converter registrations. + +| `Writer` +| Write a backend intermediate to files, text, or model bytes. + +| `Verifier` +| Run backend-specific validation after writing artifacts. +|=== + +== StableHLO alignment + +The current StableHLO implementation already follows most of this shape: + +* `StableHloConverter` is the backend converter. +* `ConversionContext` is the backend context. +* `StableHloOperationRegistry` maps operation names to converters. +* `StableHloConverterFactory` creates standard converter sets. +* `StableHloModule` is the backend result object. + +StableHLO should keep its public API and MLIR output stable while adopting shared diagnostics where useful. + +== Minerva direction + +Minerva should start as a sibling backend, not as a hidden mode of the Arduino/C99 exporter. + +The first Minerva implementation should use this shape: + +[source,text] +---- +ComputeGraph + -> MinervaCompatibilityValidator + -> MinervaLayerPatternRegistry + -> MinervaLayerPatternConverter + -> MinervaIntermediate + -> MinervaNpzWriter + -> PythonMinervaCompilerAdapter + -> MinervaProjectPackager + -> MinervaHostVerifier +---- + +Minerva should fail fast for unsupported graphs. StableHLO may keep comments or fallback paths where that behavior is already part of its compatibility story, but Minerva export should not invoke the compiler after a compatibility error. diff --git a/skainet-compile/skainet-compile-core/api/jvm/skainet-compile-core.api b/skainet-compile/skainet-compile-core/api/jvm/skainet-compile-core.api index 69c5d936f..8270967d9 100644 --- a/skainet-compile/skainet-compile-core/api/jvm/skainet-compile-core.api +++ b/skainet-compile/skainet-compile-core/api/jvm/skainet-compile-core.api @@ -1,3 +1,244 @@ +public final class sk/ainet/compile/export/GraphExportArtifact { + public fun (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportArtifactRole;Ljava/lang/String;ZLjava/util/Map;)V + public synthetic fun (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportArtifactRole;Ljava/lang/String;ZLjava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lsk/ainet/compile/export/GraphExportArtifactRole; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Z + public final fun component5 ()Ljava/util/Map; + public final fun copy (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportArtifactRole;Ljava/lang/String;ZLjava/util/Map;)Lsk/ainet/compile/export/GraphExportArtifact; + public static synthetic fun copy$default (Lsk/ainet/compile/export/GraphExportArtifact;Ljava/lang/String;Lsk/ainet/compile/export/GraphExportArtifactRole;Ljava/lang/String;ZLjava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportArtifact; + public fun equals (Ljava/lang/Object;)Z + public final fun getDescription ()Ljava/lang/String; + public final fun getMetadata ()Ljava/util/Map; + public final fun getPath ()Ljava/lang/String; + public final fun getRole ()Lsk/ainet/compile/export/GraphExportArtifactRole; + public final fun getSensitive ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class sk/ainet/compile/export/GraphExportArtifactRole : java/lang/Enum { + public static final field BINARY Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field DOCUMENTATION Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field HEADER Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field INTERMEDIATE Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field LOG Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field MANIFEST Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field PROJECT_DIRECTORY Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field SOURCE Lsk/ainet/compile/export/GraphExportArtifactRole; + public static final field TEST_REPORT Lsk/ainet/compile/export/GraphExportArtifactRole; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lsk/ainet/compile/export/GraphExportArtifactRole; + public static fun values ()[Lsk/ainet/compile/export/GraphExportArtifactRole; +} + +public final class sk/ainet/compile/export/GraphExportComponentRole : java/lang/Enum { + public static final field CONTEXT Lsk/ainet/compile/export/GraphExportComponentRole; + public static final field CONVERTER Lsk/ainet/compile/export/GraphExportComponentRole; + public static final field FACTORY Lsk/ainet/compile/export/GraphExportComponentRole; + public static final field REGISTRY Lsk/ainet/compile/export/GraphExportComponentRole; + public static final field VERIFIER Lsk/ainet/compile/export/GraphExportComponentRole; + public static final field WRITER Lsk/ainet/compile/export/GraphExportComponentRole; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getResponsibility ()Ljava/lang/String; + public final fun getSuffix ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lsk/ainet/compile/export/GraphExportComponentRole; + public static fun values ()[Lsk/ainet/compile/export/GraphExportComponentRole; +} + +public final class sk/ainet/compile/export/GraphExportContext { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun addArtifact (Lsk/ainet/compile/export/GraphExportArtifact;)Lsk/ainet/compile/export/GraphExportArtifact; + public final fun diagnosticReport ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun error (Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public static synthetic fun error$default (Lsk/ainet/compile/export/GraphExportContext;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public final fun getArtifacts ()Ljava/util/List; + public final fun getBackendName ()Ljava/lang/String; + public final fun getDiagnostics ()Ljava/util/List; + public final fun getMetadata ()Ljava/util/Map; + public final fun getTargetName ()Ljava/lang/String; + public final fun info (Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public static synthetic fun info$default (Lsk/ainet/compile/export/GraphExportContext;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public final fun report (Lsk/ainet/compile/export/GraphExportDiagnostic;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public final fun snapshot ()Lsk/ainet/compile/export/GraphExportContextSnapshot; + public final fun warning (Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public static synthetic fun warning$default (Lsk/ainet/compile/export/GraphExportContext;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportDiagnostic; +} + +public final class sk/ainet/compile/export/GraphExportContextSnapshot { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Map;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Ljava/util/List; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportContextSnapshot; + public static synthetic fun copy$default (Lsk/ainet/compile/export/GraphExportContextSnapshot;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportContextSnapshot; + public fun equals (Ljava/lang/Object;)Z + public final fun getArtifacts ()Ljava/util/List; + public final fun getBackendName ()Ljava/lang/String; + public final fun getDiagnostics ()Ljava/util/List; + public final fun getMetadata ()Ljava/util/Map; + public final fun getTargetName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class sk/ainet/compile/export/GraphExportConverter { + public abstract fun convert (Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportContext;)Ljava/lang/Object; + public abstract fun getBackendName ()Ljava/lang/String; +} + +public final class sk/ainet/compile/export/GraphExportDiagnostic { + public fun (Lsk/ainet/compile/export/GraphExportSeverity;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V + public synthetic fun (Lsk/ainet/compile/export/GraphExportSeverity;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lsk/ainet/compile/export/GraphExportSeverity; + public final fun component2 ()Lsk/ainet/compile/export/GraphExportStage; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/util/Map; + public final fun copy (Lsk/ainet/compile/export/GraphExportSeverity;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public static synthetic fun copy$default (Lsk/ainet/compile/export/GraphExportDiagnostic;Lsk/ainet/compile/export/GraphExportSeverity;Lsk/ainet/compile/export/GraphExportStage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportDiagnostic; + public fun equals (Ljava/lang/Object;)Z + public final fun getCode ()Ljava/lang/String; + public final fun getDetails ()Ljava/util/Map; + public final fun getMessage ()Ljava/lang/String; + public final fun getNodeId ()Ljava/lang/String; + public final fun getOperationName ()Ljava/lang/String; + public final fun getSeverity ()Lsk/ainet/compile/export/GraphExportSeverity; + public final fun getStage ()Lsk/ainet/compile/export/GraphExportStage; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class sk/ainet/compile/export/GraphExportDiagnosticReport { + public static final field Companion Lsk/ainet/compile/export/GraphExportDiagnosticReport$Companion; + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public static synthetic fun copy$default (Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public fun equals (Ljava/lang/Object;)Z + public final fun getDiagnostics ()Ljava/util/List; + public final fun getErrors ()Ljava/util/List; + public final fun getHasErrors ()Z + public final fun getInfos ()Ljava/util/List; + public final fun getWarnings ()Ljava/util/List; + public fun hashCode ()I + public final fun plus (Lsk/ainet/compile/export/GraphExportDiagnostic;)Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun plus (Lsk/ainet/compile/export/GraphExportDiagnosticReport;)Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun requireNoErrors ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public fun toString ()Ljava/lang/String; +} + +public final class sk/ainet/compile/export/GraphExportDiagnosticReport$Companion { + public final fun empty ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; +} + +public final class sk/ainet/compile/export/GraphExportResult { + public static final field Companion Lsk/ainet/compile/export/GraphExportResult$Companion; + public fun (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportStatus;Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;)V + public synthetic fun (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportStatus;Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lsk/ainet/compile/export/GraphExportStatus; + public final fun component3 ()Ljava/lang/Object; + public final fun component4 ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun component5 ()Ljava/util/List; + public final fun component6 ()Ljava/util/Map; + public final fun copy (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportStatus;Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportResult; + public static synthetic fun copy$default (Lsk/ainet/compile/export/GraphExportResult;Ljava/lang/String;Lsk/ainet/compile/export/GraphExportStatus;Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportResult; + public fun equals (Ljava/lang/Object;)Z + public final fun getArtifacts ()Ljava/util/List; + public final fun getBackendName ()Ljava/lang/String; + public final fun getDiagnostics ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun getFailed ()Z + public final fun getMetadata ()Ljava/util/Map; + public final fun getOutput ()Ljava/lang/Object; + public final fun getStatus ()Lsk/ainet/compile/export/GraphExportStatus; + public final fun getSucceeded ()Z + public fun hashCode ()I + public final fun requireSuccess ()Ljava/lang/Object; + public fun toString ()Ljava/lang/String; +} + +public final class sk/ainet/compile/export/GraphExportResult$Companion { + public final fun failure (Ljava/lang/String;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportResult; + public static synthetic fun failure$default (Lsk/ainet/compile/export/GraphExportResult$Companion;Ljava/lang/String;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportResult; + public final fun skipped (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportResult; + public static synthetic fun skipped$default (Lsk/ainet/compile/export/GraphExportResult$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportResult; + public final fun success (Ljava/lang/String;Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportResult; + public static synthetic fun success$default (Lsk/ainet/compile/export/GraphExportResult$Companion;Ljava/lang/String;Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportResult; +} + +public final class sk/ainet/compile/export/GraphExportSeverity : java/lang/Enum { + public static final field ERROR Lsk/ainet/compile/export/GraphExportSeverity; + public static final field INFO Lsk/ainet/compile/export/GraphExportSeverity; + public static final field WARNING Lsk/ainet/compile/export/GraphExportSeverity; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lsk/ainet/compile/export/GraphExportSeverity; + public static fun values ()[Lsk/ainet/compile/export/GraphExportSeverity; +} + +public final class sk/ainet/compile/export/GraphExportStage : java/lang/Enum { + public static final field CAPTURE Lsk/ainet/compile/export/GraphExportStage; + public static final field Companion Lsk/ainet/compile/export/GraphExportStage$Companion; + public static final field LOWERING Lsk/ainet/compile/export/GraphExportStage; + public static final field PACKAGING Lsk/ainet/compile/export/GraphExportStage; + public static final field VALIDATION Lsk/ainet/compile/export/GraphExportStage; + public static final field VERIFICATION Lsk/ainet/compile/export/GraphExportStage; + public static final field WRITING Lsk/ainet/compile/export/GraphExportStage; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lsk/ainet/compile/export/GraphExportStage; + public static fun values ()[Lsk/ainet/compile/export/GraphExportStage; +} + +public final class sk/ainet/compile/export/GraphExportStage$Companion { + public final fun getDefaultOrder ()Ljava/util/List; +} + +public final class sk/ainet/compile/export/GraphExportStatus : java/lang/Enum { + public static final field FAILED Lsk/ainet/compile/export/GraphExportStatus; + public static final field SKIPPED Lsk/ainet/compile/export/GraphExportStatus; + public static final field SUCCESS Lsk/ainet/compile/export/GraphExportStatus; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lsk/ainet/compile/export/GraphExportStatus; + public static fun values ()[Lsk/ainet/compile/export/GraphExportStatus; +} + +public final class sk/ainet/compile/export/GraphExportVerification { + public fun (ZLsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;)V + public synthetic fun (ZLsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Ljava/util/Map; + public final fun copy (ZLsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;)Lsk/ainet/compile/export/GraphExportVerification; + public static synthetic fun copy$default (Lsk/ainet/compile/export/GraphExportVerification;ZLsk/ainet/compile/export/GraphExportDiagnosticReport;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lsk/ainet/compile/export/GraphExportVerification; + public fun equals (Ljava/lang/Object;)Z + public final fun getArtifacts ()Ljava/util/List; + public final fun getDiagnostics ()Lsk/ainet/compile/export/GraphExportDiagnosticReport; + public final fun getMetadata ()Ljava/util/Map; + public final fun getPassed ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class sk/ainet/compile/export/GraphExportVerifier { + public abstract fun getBackendName ()Ljava/lang/String; + public abstract fun verify (Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportContext;)Lsk/ainet/compile/export/GraphExportVerification; +} + +public abstract interface class sk/ainet/compile/export/GraphExportWriter { + public abstract fun getBackendName ()Ljava/lang/String; + public abstract fun write (Ljava/lang/Object;Lsk/ainet/compile/export/GraphExportContext;)Ljava/lang/Object; +} + public final class sk/ainet/compile/nn/DefaultNetworkContext : sk/ainet/compile/nn/NeuralNetworkContext { public fun ()V public fun getExecutionContext ()Lsk/ainet/lang/nn/NeuralNetworkExecutionContext; diff --git a/skainet-compile/skainet-compile-core/src/commonMain/kotlin/sk/ainet/compile/export/GraphExportContracts.kt b/skainet-compile/skainet-compile-core/src/commonMain/kotlin/sk/ainet/compile/export/GraphExportContracts.kt new file mode 100644 index 000000000..c573b16b1 --- /dev/null +++ b/skainet-compile/skainet-compile-core/src/commonMain/kotlin/sk/ainet/compile/export/GraphExportContracts.kt @@ -0,0 +1,405 @@ +package sk.ainet.compile.export + +/** + * Stage of a graph export workflow. + * + * Backends do not need to implement every stage. The enum exists so StableHLO, + * Minerva, and later exporters can report comparable diagnostics while keeping + * their writer implementations separate. + */ +public enum class GraphExportStage { + CAPTURE, + VALIDATION, + LOWERING, + WRITING, + PACKAGING, + VERIFICATION; + + public companion object { + public val defaultOrder: List = listOf( + CAPTURE, + VALIDATION, + LOWERING, + WRITING, + PACKAGING, + VERIFICATION + ) + } +} + +/** + * Severity for export diagnostics. + */ +public enum class GraphExportSeverity { + INFO, + WARNING, + ERROR +} + +/** + * Shared diagnostic record for graph exporters. + * + * The optional [nodeId] and [operationName] fields let backends tie a finding + * to graph-level source information without making compile-core depend on the + * DAG module. + */ +public data class GraphExportDiagnostic( + public val severity: GraphExportSeverity, + public val stage: GraphExportStage, + public val code: String, + public val message: String, + public val nodeId: String? = null, + public val operationName: String? = null, + public val details: Map = emptyMap() +) { + init { + require(code.isNotBlank()) { "Diagnostic code cannot be blank" } + require(message.isNotBlank()) { "Diagnostic message cannot be blank" } + } +} + +/** + * Immutable collection of export diagnostics with convenience views. + */ +public data class GraphExportDiagnosticReport( + public val diagnostics: List = emptyList() +) { + public val errors: List + get() = diagnostics.filter { it.severity == GraphExportSeverity.ERROR } + + public val warnings: List + get() = diagnostics.filter { it.severity == GraphExportSeverity.WARNING } + + public val infos: List + get() = diagnostics.filter { it.severity == GraphExportSeverity.INFO } + + public val hasErrors: Boolean + get() = errors.isNotEmpty() + + public operator fun plus(diagnostic: GraphExportDiagnostic): GraphExportDiagnosticReport { + return copy(diagnostics = diagnostics + diagnostic) + } + + public operator fun plus(other: GraphExportDiagnosticReport): GraphExportDiagnosticReport { + return copy(diagnostics = diagnostics + other.diagnostics) + } + + public fun requireNoErrors(): GraphExportDiagnosticReport { + if (hasErrors) { + val summary = errors.joinToString("; ") { "${it.code}: ${it.message}" } + error("Graph export reported errors: $summary") + } + return this + } + + public companion object { + public fun empty(): GraphExportDiagnosticReport = GraphExportDiagnosticReport() + } +} + +/** + * Role for a generated or consumed export artifact. + */ +public enum class GraphExportArtifactRole { + INTERMEDIATE, + SOURCE, + HEADER, + MANIFEST, + PROJECT_DIRECTORY, + LOG, + TEST_REPORT, + BINARY, + DOCUMENTATION +} + +/** + * Portable artifact descriptor. + * + * Paths are strings so this type remains usable from common source sets. + */ +public data class GraphExportArtifact( + public val path: String, + public val role: GraphExportArtifactRole, + public val description: String = "", + public val sensitive: Boolean = false, + public val metadata: Map = emptyMap() +) { + init { + require(path.isNotBlank()) { "Artifact path cannot be blank" } + } +} + +/** + * Overall status for a backend export. + */ +public enum class GraphExportStatus { + SUCCESS, + FAILED, + SKIPPED +} + +/** + * Shared result envelope for export backends. + * + * The [output] type is backend-specific: StableHLO can use a module object, + * Minerva can use an export bundle, and future backends can choose their own + * writer result while sharing diagnostics and artifact metadata. + */ +public data class GraphExportResult( + public val backendName: String, + public val status: GraphExportStatus, + public val output: T? = null, + public val diagnostics: GraphExportDiagnosticReport = GraphExportDiagnosticReport.empty(), + public val artifacts: List = emptyList(), + public val metadata: Map = emptyMap() +) { + init { + require(backendName.isNotBlank()) { "backendName cannot be blank" } + } + + public val succeeded: Boolean + get() = status == GraphExportStatus.SUCCESS + + public val failed: Boolean + get() = status == GraphExportStatus.FAILED + + public fun requireSuccess(): T { + if (!succeeded) { + val summary = diagnostics.diagnostics.joinToString("; ") { "${it.code}: ${it.message}" } + error("Graph export for $backendName did not succeed: $summary") + } + return output ?: error("Graph export for $backendName succeeded without an output") + } + + public companion object { + public fun success( + backendName: String, + output: T, + diagnostics: GraphExportDiagnosticReport = GraphExportDiagnosticReport.empty(), + artifacts: List = emptyList(), + metadata: Map = emptyMap() + ): GraphExportResult { + return GraphExportResult( + backendName = backendName, + status = GraphExportStatus.SUCCESS, + output = output, + diagnostics = diagnostics, + artifacts = artifacts, + metadata = metadata + ) + } + + public fun failure( + backendName: String, + diagnostics: GraphExportDiagnosticReport, + artifacts: List = emptyList(), + metadata: Map = emptyMap() + ): GraphExportResult { + return GraphExportResult( + backendName = backendName, + status = GraphExportStatus.FAILED, + output = null, + diagnostics = diagnostics, + artifacts = artifacts, + metadata = metadata + ) + } + + public fun skipped( + backendName: String, + reason: String, + metadata: Map = emptyMap() + ): GraphExportResult { + val report = GraphExportDiagnosticReport( + listOf( + GraphExportDiagnostic( + severity = GraphExportSeverity.INFO, + stage = GraphExportStage.VALIDATION, + code = "export.skipped", + message = reason + ) + ) + ) + return GraphExportResult( + backendName = backendName, + status = GraphExportStatus.SKIPPED, + output = null, + diagnostics = report, + metadata = metadata + ) + } + } +} + +/** + * Mutable context shared across validation, lowering, writing, packaging, and + * verification phases. + */ +public class GraphExportContext( + public val backendName: String, + public val targetName: String? = null, + public val metadata: Map = emptyMap() +) { + init { + require(backendName.isNotBlank()) { "backendName cannot be blank" } + } + + private val mutableDiagnostics: MutableList = mutableListOf() + private val mutableArtifacts: MutableList = mutableListOf() + + public val diagnostics: List + get() = mutableDiagnostics.toList() + + public val artifacts: List + get() = mutableArtifacts.toList() + + public fun report(diagnostic: GraphExportDiagnostic): GraphExportDiagnostic { + mutableDiagnostics += diagnostic + return diagnostic + } + + public fun info( + stage: GraphExportStage, + code: String, + message: String, + nodeId: String? = null, + operationName: String? = null, + details: Map = emptyMap() + ): GraphExportDiagnostic { + return report( + GraphExportDiagnostic( + severity = GraphExportSeverity.INFO, + stage = stage, + code = code, + message = message, + nodeId = nodeId, + operationName = operationName, + details = details + ) + ) + } + + public fun warning( + stage: GraphExportStage, + code: String, + message: String, + nodeId: String? = null, + operationName: String? = null, + details: Map = emptyMap() + ): GraphExportDiagnostic { + return report( + GraphExportDiagnostic( + severity = GraphExportSeverity.WARNING, + stage = stage, + code = code, + message = message, + nodeId = nodeId, + operationName = operationName, + details = details + ) + ) + } + + public fun error( + stage: GraphExportStage, + code: String, + message: String, + nodeId: String? = null, + operationName: String? = null, + details: Map = emptyMap() + ): GraphExportDiagnostic { + return report( + GraphExportDiagnostic( + severity = GraphExportSeverity.ERROR, + stage = stage, + code = code, + message = message, + nodeId = nodeId, + operationName = operationName, + details = details + ) + ) + } + + public fun addArtifact(artifact: GraphExportArtifact): GraphExportArtifact { + mutableArtifacts += artifact + return artifact + } + + public fun diagnosticReport(): GraphExportDiagnosticReport { + return GraphExportDiagnosticReport(mutableDiagnostics.toList()) + } + + public fun snapshot(): GraphExportContextSnapshot { + return GraphExportContextSnapshot( + backendName = backendName, + targetName = targetName, + diagnostics = mutableDiagnostics.toList(), + artifacts = mutableArtifacts.toList(), + metadata = metadata + ) + } +} + +/** + * Immutable view of a [GraphExportContext]. + */ +public data class GraphExportContextSnapshot( + public val backendName: String, + public val targetName: String?, + public val diagnostics: List, + public val artifacts: List, + public val metadata: Map = emptyMap() +) + +/** + * Converts a source representation into a backend-specific intermediate. + */ +public interface GraphExportConverter { + public val backendName: String + + public fun convert(input: Input, context: GraphExportContext): Intermediate +} + +/** + * Writes a backend-specific intermediate into its final export output. + */ +public interface GraphExportWriter { + public val backendName: String + + public fun write(intermediate: Intermediate, context: GraphExportContext): Output +} + +/** + * Verification result for an exported backend output. + */ +public data class GraphExportVerification( + public val passed: Boolean, + public val diagnostics: GraphExportDiagnosticReport = GraphExportDiagnosticReport.empty(), + public val artifacts: List = emptyList(), + public val metadata: Map = emptyMap() +) + +/** + * Verifies an exported backend output. + */ +public interface GraphExportVerifier { + public val backendName: String + + public fun verify(output: Output, context: GraphExportContext): GraphExportVerification +} + +/** + * Canonical component names for graph export implementations. + */ +public enum class GraphExportComponentRole( + public val suffix: String, + public val responsibility: String +) { + CONVERTER("Converter", "Lower source graph structures into a backend intermediate."), + CONTEXT("Context", "Carry backend state, diagnostics, and generated artifacts."), + REGISTRY("Registry", "Map operation names or graph patterns to backend converters."), + FACTORY("Factory", "Construct a backend exporter with standard converter registrations."), + WRITER("Writer", "Write a backend intermediate to files, text, or model bytes."), + VERIFIER("Verifier", "Run backend-specific validation after writing artifacts.") +} diff --git a/skainet-compile/skainet-compile-core/src/commonTest/kotlin/sk/ainet/compile/export/GraphExportContractsTest.kt b/skainet-compile/skainet-compile-core/src/commonTest/kotlin/sk/ainet/compile/export/GraphExportContractsTest.kt new file mode 100644 index 000000000..337f62d74 --- /dev/null +++ b/skainet-compile/skainet-compile-core/src/commonTest/kotlin/sk/ainet/compile/export/GraphExportContractsTest.kt @@ -0,0 +1,146 @@ +package sk.ainet.compile.export + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class GraphExportContractsTest { + @Test + fun diagnosticReportSeparatesSeverities() { + val report = GraphExportDiagnosticReport.empty() + .plus( + GraphExportDiagnostic( + severity = GraphExportSeverity.INFO, + stage = GraphExportStage.CAPTURE, + code = "capture.started", + message = "capture started" + ) + ) + .plus( + GraphExportDiagnostic( + severity = GraphExportSeverity.WARNING, + stage = GraphExportStage.VALIDATION, + code = "validation.fallback", + message = "fallback path used" + ) + ) + .plus( + GraphExportDiagnostic( + severity = GraphExportSeverity.ERROR, + stage = GraphExportStage.LOWERING, + code = "lowering.unsupported", + message = "unsupported pattern", + nodeId = "n1", + operationName = "reshape" + ) + ) + + assertEquals(1, report.infos.size) + assertEquals(1, report.warnings.size) + assertEquals(1, report.errors.size) + assertTrue(report.hasErrors) + assertEquals("n1", report.errors.single().nodeId) + } + + @Test + fun diagnosticReportRequireNoErrorsThrowsForErrors() { + val report = GraphExportDiagnosticReport( + listOf( + GraphExportDiagnostic( + severity = GraphExportSeverity.ERROR, + stage = GraphExportStage.VALIDATION, + code = "validation.unsupported", + message = "unsupported operation" + ) + ) + ) + + assertFailsWith { + report.requireNoErrors() + } + } + + @Test + fun contextCollectsDiagnosticsAndArtifacts() { + val context = GraphExportContext( + backendName = "minerva", + targetName = "atmega328p", + metadata = mapOf("quantization" to "q8") + ) + + context.warning( + stage = GraphExportStage.VALIDATION, + code = "validation.experimental", + message = "backend is experimental" + ) + context.addArtifact( + GraphExportArtifact( + path = "build/minerva/model.npz", + role = GraphExportArtifactRole.INTERMEDIATE, + description = "Minerva compiler input" + ) + ) + + val snapshot = context.snapshot() + + assertEquals("minerva", snapshot.backendName) + assertEquals("atmega328p", snapshot.targetName) + assertEquals(1, snapshot.diagnostics.size) + assertEquals(1, snapshot.artifacts.size) + assertEquals("q8", snapshot.metadata["quantization"]) + assertFalse(context.diagnosticReport().hasErrors) + } + + @Test + fun resultRequireSuccessReturnsBackendOutput() { + val result = GraphExportResult.success( + backendName = "stablehlo", + output = "module { }", + artifacts = listOf( + GraphExportArtifact( + path = "build/model.mlir", + role = GraphExportArtifactRole.SOURCE + ) + ) + ) + + assertTrue(result.succeeded) + assertEquals("module { }", result.requireSuccess()) + assertEquals(GraphExportStatus.SUCCESS, result.status) + assertEquals(1, result.artifacts.size) + } + + @Test + fun resultRequireSuccessThrowsForFailure() { + val result = GraphExportResult.failure( + backendName = "minerva", + diagnostics = GraphExportDiagnosticReport( + listOf( + GraphExportDiagnostic( + severity = GraphExportSeverity.ERROR, + stage = GraphExportStage.VALIDATION, + code = "validation.unsupported", + message = "unsupported graph" + ) + ) + ) + ) + + assertTrue(result.failed) + assertFailsWith { + result.requireSuccess() + } + } + + @Test + fun componentRolesDocumentNamingConventions() { + assertEquals("Converter", GraphExportComponentRole.CONVERTER.suffix) + assertEquals("Context", GraphExportComponentRole.CONTEXT.suffix) + assertEquals("Registry", GraphExportComponentRole.REGISTRY.suffix) + assertEquals("Factory", GraphExportComponentRole.FACTORY.suffix) + assertEquals("Writer", GraphExportComponentRole.WRITER.suffix) + assertEquals("Verifier", GraphExportComponentRole.VERIFIER.suffix) + } +}