@mlx-bun/inference 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1475 -2
- package/THIRD_PARTY_NOTICES.md +3404 -0
- package/dist/native/libmlx_bun_ane.dylib +0 -0
- package/dist/native/libmlx_bun_expert_io.dylib +0 -0
- package/dist/native/mlx-bun-frame-extract +0 -0
- package/examples/fit-model.ts +25 -0
- package/examples/qwen3-forward.ts +42 -0
- package/examples/qwen3-generate.ts +28 -0
- package/examples/trellis-expand.ts +23 -0
- package/package.json +129 -4
- package/src/adapters/available.ts +110 -0
- package/src/adapters/index.ts +3 -0
- package/src/adapters/manager.ts +316 -0
- package/src/artifacts/auxiliary-files.ts +45 -0
- package/src/artifacts/config-dialects.ts +94 -0
- package/src/artifacts/config.ts +341 -0
- package/src/artifacts/draft-vocabulary.ts +30 -0
- package/src/artifacts/expert-offload-build.ts +75 -0
- package/src/artifacts/expert-offload.ts +89 -0
- package/src/artifacts/experts/index.ts +3 -0
- package/src/artifacts/experts/io.ts +386 -0
- package/src/artifacts/experts/residency.ts +1134 -0
- package/src/artifacts/experts/usage.ts +415 -0
- package/src/artifacts/fingerprint.ts +44 -0
- package/src/artifacts/glm52/container.ts +305 -0
- package/src/artifacts/glm52/expert-layout.ts +249 -0
- package/src/artifacts/glm52/index.ts +7 -0
- package/src/artifacts/glm52/kernel-layout.ts +124 -0
- package/src/artifacts/glm52/layout.ts +152 -0
- package/src/artifacts/glm52/memory.ts +500 -0
- package/src/artifacts/glm52/resident-weights.ts +135 -0
- package/src/artifacts/glm52/weights.ts +503 -0
- package/src/artifacts/glm52-config.ts +262 -0
- package/src/artifacts/identity.ts +94 -0
- package/src/artifacts/index.ts +15 -0
- package/src/artifacts/mmap.ts +90 -0
- package/src/artifacts/safetensors-writer.ts +249 -0
- package/src/artifacts/safetensors.ts +164 -0
- package/src/artifacts/turboquant-fold.ts +42 -0
- package/src/artifacts/weight-audit.ts +43 -0
- package/src/artifacts/weights.ts +255 -0
- package/src/contracts/index.ts +2 -0
- package/src/contracts/mlx/attention.ts +8 -0
- package/src/contracts/mlx/cache.ts +510 -0
- package/src/contracts/mlx/checkpoint.ts +10 -0
- package/src/contracts/mlx/draft-checkpoint.ts +14 -0
- package/src/contracts/mlx/draft-projection.ts +27 -0
- package/src/contracts/mlx/draft-target.ts +84 -0
- package/src/contracts/mlx/drafter.ts +176 -0
- package/src/contracts/mlx/forward-work.ts +23 -0
- package/src/contracts/mlx/glm52-weights.ts +25 -0
- package/src/contracts/mlx/graph.ts +309 -0
- package/src/contracts/mlx/head.ts +6 -0
- package/src/contracts/mlx/index.ts +15 -0
- package/src/contracts/mlx/kv-codec.ts +9 -0
- package/src/contracts/mlx/media.ts +113 -0
- package/src/contracts/mlx/positions.ts +16 -0
- package/src/contracts/mlx/row-table.ts +2 -0
- package/src/contracts/mlx/trainable.ts +103 -0
- package/src/contracts/mlx/whisper.ts +96 -0
- package/src/contracts/portable/composition.ts +106 -0
- package/src/contracts/portable/denoising.ts +17 -0
- package/src/contracts/portable/execution.ts +79 -0
- package/src/contracts/portable/generation.ts +103 -0
- package/src/contracts/portable/graph.ts +80 -0
- package/src/contracts/portable/index.ts +15 -0
- package/src/contracts/portable/memory-plan.ts +26 -0
- package/src/contracts/portable/object-cache.ts +8 -0
- package/src/contracts/portable/prefill.ts +63 -0
- package/src/contracts/portable/prefix-cache.ts +33 -0
- package/src/contracts/portable/prompt.ts +2 -0
- package/src/contracts/portable/resources.ts +25 -0
- package/src/contracts/portable/rollback.ts +7 -0
- package/src/contracts/portable/sampling.ts +44 -0
- package/src/contracts/portable/scheduling.ts +54 -0
- package/src/contracts/portable/token-delivery.ts +4 -0
- package/src/contracts/portable/tsconfig.json +14 -0
- package/src/embeddings/index.ts +1 -0
- package/src/embeddings/text.ts +85 -0
- package/src/execution/admission.ts +59 -0
- package/src/execution/batch-group.ts +1240 -0
- package/src/execution/batch-types.ts +225 -0
- package/src/execution/cancellation.ts +38 -0
- package/src/execution/continuation-request.ts +45 -0
- package/src/execution/continuation-types.ts +10 -0
- package/src/execution/coordinator.ts +70 -0
- package/src/execution/denoising-group.ts +180 -0
- package/src/execution/engine.ts +56 -0
- package/src/execution/fill-group.ts +505 -0
- package/src/execution/fit.ts +184 -0
- package/src/execution/gateway-binding.ts +178 -0
- package/src/execution/grammar-group.ts +224 -0
- package/src/execution/index.ts +18 -0
- package/src/execution/methods.ts +75 -0
- package/src/execution/mixed-iteration.ts +96 -0
- package/src/execution/plan.ts +65 -0
- package/src/execution/prefill-cohort.ts +100 -0
- package/src/execution/prefill-rows.ts +263 -0
- package/src/execution/request-policy.ts +19 -0
- package/src/execution/row-sampling.ts +49 -0
- package/src/execution/scheduler.ts +92 -0
- package/src/execution/session.ts +247 -0
- package/src/execution/speculative-group.ts +797 -0
- package/src/execution/tasks.ts +44 -0
- package/src/generation/autoregressive.ts +840 -0
- package/src/generation/bindings/autoregressive.ts +104 -0
- package/src/generation/bindings/denoising.ts +60 -0
- package/src/generation/bindings/prefill-policy.ts +36 -0
- package/src/generation/checkpoint-identity.ts +60 -0
- package/src/generation/denoising.ts +85 -0
- package/src/generation/diffusion.ts +583 -0
- package/src/generation/fill/echo-index.ts +188 -0
- package/src/generation/fill/index.ts +3 -0
- package/src/generation/fill/proposal.ts +69 -0
- package/src/generation/fill/schema-rows.ts +506 -0
- package/src/generation/fill/session.ts +464 -0
- package/src/generation/fill/tool-boundary.ts +103 -0
- package/src/generation/fill-append.ts +58 -0
- package/src/generation/generate.ts +44 -0
- package/src/generation/grammar-step.ts +39 -0
- package/src/generation/index.ts +14 -0
- package/src/generation/phase-dispatch.ts +115 -0
- package/src/generation/prefill.ts +33 -0
- package/src/generation/result.ts +49 -0
- package/src/generation/scopes.ts +157 -0
- package/src/generation/speculative/acceptance.ts +41 -0
- package/src/generation/speculative/adaptive-draft-gate.ts +166 -0
- package/src/generation/speculative/bindings/assistant-rows.ts +102 -0
- package/src/generation/speculative/bindings/autoregressive-rows.ts +124 -0
- package/src/generation/speculative/bindings/binding.ts +106 -0
- package/src/generation/speculative/bindings/deepspec-rows.ts +33 -0
- package/src/generation/speculative/bindings/draft-target.ts +10 -0
- package/src/generation/speculative/bindings/glm52-mtp-rows.ts +128 -0
- package/src/generation/speculative/bindings/projected-context-rows.ts +78 -0
- package/src/generation/speculative/bindings/projected-draft-rows.ts +95 -0
- package/src/generation/speculative/bindings/qwen-mtp-rows.ts +336 -0
- package/src/generation/speculative/bindings/two-model-rows.ts +63 -0
- package/src/generation/speculative/cache-identity.ts +16 -0
- package/src/generation/speculative/draft-kind.ts +149 -0
- package/src/generation/speculative/draft-policy.ts +41 -0
- package/src/generation/speculative/draft-registry.ts +118 -0
- package/src/generation/speculative/draft-vocab.ts +110 -0
- package/src/generation/speculative/index.ts +17 -0
- package/src/generation/speculative/output.ts +49 -0
- package/src/generation/speculative/round.ts +307 -0
- package/src/generation/speculative/source.ts +144 -0
- package/src/generation/speculative/sources/assistant-source.ts +64 -0
- package/src/generation/speculative/sources/deepspec-source.ts +70 -0
- package/src/generation/speculative/sources/dflash-source.ts +84 -0
- package/src/generation/speculative/sources/dflash2-source.ts +86 -0
- package/src/generation/speculative/sources/glm52-mtp-source.ts +66 -0
- package/src/generation/speculative/sources/ngram-source.ts +165 -0
- package/src/generation/speculative/sources/qwen-mtp-source.ts +154 -0
- package/src/generation/speculative/sources/two-model.ts +75 -0
- package/src/generation/types.ts +194 -0
- package/src/index.ts +20 -0
- package/src/input/audio/audiotoolbox.ts +112 -0
- package/src/input/audio/decode.ts +133 -0
- package/src/input/audio/features.ts +202 -0
- package/src/input/audio/index.ts +6 -0
- package/src/input/audio/transcode.ts +67 -0
- package/src/input/audio/whisper-mel.ts +185 -0
- package/src/input/audio/whisper-tokenizer.ts +238 -0
- package/src/input/chat-template.ts +389 -0
- package/src/input/index.ts +6 -0
- package/src/input/media-fetch.ts +439 -0
- package/src/input/packed-mixed.ts +43 -0
- package/src/input/special-tokens.ts +75 -0
- package/src/input/token-groups.ts +49 -0
- package/src/input/tokenizer.ts +336 -0
- package/src/input/tool-call.ts +689 -0
- package/src/input/vision/index.ts +3 -0
- package/src/input/vision/preprocess.ts +247 -0
- package/src/input/vision/prompt.ts +398 -0
- package/src/input/vision/qwen3vl-preprocess.ts +409 -0
- package/src/input/vision/qwen3vl-prompt.ts +237 -0
- package/src/input/vision/video-frames.ts +100 -0
- package/src/kernels/ane/fill.ts +101 -0
- package/src/kernels/ane/linear.ts +416 -0
- package/src/kernels/attention/flash.ts +694 -0
- package/src/kernels/attention/kvq4-decode.ts +155 -0
- package/src/kernels/attention/masks.ts +40 -0
- package/src/kernels/attention/multi-query.ts +193 -0
- package/src/kernels/attention/paged.ts +130 -0
- package/src/kernels/delta/gated.ts +306 -0
- package/src/kernels/delta/index.ts +3 -0
- package/src/kernels/delta/silu-rms-heads.ts +69 -0
- package/src/kernels/delta/state.ts +74 -0
- package/src/kernels/gated-rms-norm.ts +64 -0
- package/src/kernels/glm52/layout.ts +37 -0
- package/src/kernels/glm52/streamed-experts.ts +360 -0
- package/src/kernels/logits.ts +38 -0
- package/src/kernels/quantization/affine-row.ts +154 -0
- package/src/kernels/quantization/affine-rows.ts +36 -0
- package/src/kernels/quantization/affine-split-mma.ts +144 -0
- package/src/kernels/quantization/affine3-mma.ts +117 -0
- package/src/kernels/quantization/affine3-rows.ts +103 -0
- package/src/kernels/quantization/affine4-mma.ts +113 -0
- package/src/kernels/quantization/affine4-rows.ts +102 -0
- package/src/kernels/residual-rms-norm.ts +65 -0
- package/src/kernels/sampling/index.ts +2 -0
- package/src/kernels/sampling/normalized-argmax.ts +116 -0
- package/src/kernels/sampling/token-bitmask.ts +52 -0
- package/src/kernels/softcap.ts +35 -0
- package/src/kernels/trellis/codebook.ts +123 -0
- package/src/kernels/trellis/down-codebook.ts +184 -0
- package/src/kernels/trellis/down-factored.ts +180 -0
- package/src/kernels/trellis/down-k3-interleaved-factored.ts +146 -0
- package/src/kernels/trellis/down-mma-wide.ts +195 -0
- package/src/kernels/trellis/down-mma.ts +145 -0
- package/src/kernels/trellis/expand.ts +42 -0
- package/src/kernels/trellis/gate-up-codebook-mma.ts +141 -0
- package/src/kernels/trellis/gate-up-codebook.ts +137 -0
- package/src/kernels/trellis/gate-up-factored.ts +197 -0
- package/src/kernels/trellis/gate-up-mma-wide.ts +147 -0
- package/src/kernels/trellis/gate-up-mma.ts +148 -0
- package/src/kernels/trellis/gate-up.ts +248 -0
- package/src/kernels/trellis/geometry.ts +22 -0
- package/src/kernels/trellis/index.ts +103 -0
- package/src/kernels/trellis/launch.ts +12 -0
- package/src/kernels/trellis/mixed-gate-up-factored.ts +119 -0
- package/src/kernels/trellis/mixed-gate-up.ts +146 -0
- package/src/kernels/trellis/qualified.ts +34 -0
- package/src/kernels/trellis/reduce.ts +150 -0
- package/src/kernels/trellis/scatter.ts +407 -0
- package/src/kernels/trellis/splitk-prefill.ts +152 -0
- package/src/kernels/trellis/tiled-prefill.ts +131 -0
- package/src/kernels/trellis/vector-expand.ts +58 -0
- package/src/kernels/trellis/wide-prefill.ts +89 -0
- package/src/kernels/turboquant/decode.ts +82 -0
- package/src/kernels/turboquant/index.ts +5 -0
- package/src/kernels/turboquant/inverse.ts +122 -0
- package/src/kernels/turboquant/ops.ts +860 -0
- package/src/kernels/turboquant/signs.ts +130 -0
- package/src/kernels/turboquant/tables.ts +124 -0
- package/src/layers/affine-verify-linear.ts +75 -0
- package/src/layers/ane-prefill-split.ts +283 -0
- package/src/layers/compiled-normalization.ts +41 -0
- package/src/layers/delta-heads.ts +23 -0
- package/src/layers/dense-embedding.ts +29 -0
- package/src/layers/dense-linear.ts +32 -0
- package/src/layers/dequantized-linear.ts +72 -0
- package/src/layers/folded-attention.ts +42 -0
- package/src/layers/geglu.ts +130 -0
- package/src/layers/helpers.ts +7 -0
- package/src/layers/index.ts +36 -0
- package/src/layers/kv4-decode-attention.ts +15 -0
- package/src/layers/layer-norm.ts +24 -0
- package/src/layers/loading.ts +92 -0
- package/src/layers/lora.ts +150 -0
- package/src/layers/normalization.ts +9 -0
- package/src/layers/prefix-layout.ts +33 -0
- package/src/layers/quantized-append-attention.ts +32 -0
- package/src/layers/quantized-attention.ts +353 -0
- package/src/layers/quantized-embedding.ts +39 -0
- package/src/layers/quantized-linear.ts +137 -0
- package/src/layers/quantized-switch-linear.ts +37 -0
- package/src/layers/qwen-append.ts +12 -0
- package/src/layers/qwen-mrope.ts +238 -0
- package/src/layers/rope-2d.ts +110 -0
- package/src/layers/rope.ts +389 -0
- package/src/layers/swiglu.ts +23 -0
- package/src/layers/trellis-codebook-down.ts +52 -0
- package/src/layers/trellis-codebook-gate-up.ts +42 -0
- package/src/layers/trellis-down.ts +117 -0
- package/src/layers/trellis-gate-up.ts +71 -0
- package/src/layers/trellis-linear.ts +211 -0
- package/src/layers/trellis-prompt.ts +102 -0
- package/src/layers/trellis-reduce-gate-up.ts +73 -0
- package/src/layers/trellis-scatter-down.ts +98 -0
- package/src/models/ane-install.ts +74 -0
- package/src/models/audio/conformer.ts +656 -0
- package/src/models/cache-composition.ts +44 -0
- package/src/models/capabilities.ts +89 -0
- package/src/models/chat-template.ts +10 -0
- package/src/models/composition.ts +107 -0
- package/src/models/diffusion-gemma/experts.ts +59 -0
- package/src/models/diffusion-gemma/family.ts +21 -0
- package/src/models/diffusion-gemma/model.ts +900 -0
- package/src/models/diffusion-gemma/vision.ts +549 -0
- package/src/models/drafter-loaders.ts +49 -0
- package/src/models/drafters.ts +39 -0
- package/src/models/factory.ts +351 -0
- package/src/models/families.ts +70 -0
- package/src/models/family.ts +125 -0
- package/src/models/gemma4/assistant-target.ts +46 -0
- package/src/models/gemma4/assistant.ts +327 -0
- package/src/models/gemma4/blocks.ts +755 -0
- package/src/models/gemma4/compiled-decode.ts +452 -0
- package/src/models/gemma4/family.ts +39 -0
- package/src/models/gemma4/gemma4-12b.ts +677 -0
- package/src/models/gemma4/gemma4-26b.ts +544 -0
- package/src/models/gemma4/gemma4-e4b.ts +599 -0
- package/src/models/gemma4/graphs.ts +147 -0
- package/src/models/gemma4/media.ts +88 -0
- package/src/models/gemma4/model.ts +860 -0
- package/src/models/gemma4/serving-blocks.ts +72 -0
- package/src/models/gemma4/trainable.ts +186 -0
- package/src/models/gemma4/written-blocks.ts +232 -0
- package/src/models/gemma4/written-forward.ts +159 -0
- package/src/models/gemma4/written-input.ts +29 -0
- package/src/models/glm52/atlas.ts +507 -0
- package/src/models/glm52/coupling.ts +388 -0
- package/src/models/glm52/dsa.ts +601 -0
- package/src/models/glm52/family.ts +20 -0
- package/src/models/glm52/mla.ts +812 -0
- package/src/models/glm52/model.ts +1127 -0
- package/src/models/glm52/moe.ts +223 -0
- package/src/models/glm52/mtp.ts +60 -0
- package/src/models/glm52/pilot.ts +537 -0
- package/src/models/glm52/reference.ts +445 -0
- package/src/models/glm52/residency.ts +557 -0
- package/src/models/glm52/streamed-experts.ts +447 -0
- package/src/models/graph.ts +59 -0
- package/src/models/implementation.ts +230 -0
- package/src/models/index.ts +15 -0
- package/src/models/media-input.ts +15 -0
- package/src/models/memory-plan.ts +39 -0
- package/src/models/minicpm5/family.ts +29 -0
- package/src/models/minicpm5/model.ts +394 -0
- package/src/models/minicpm5/trainable.ts +90 -0
- package/src/models/profile.ts +337 -0
- package/src/models/qwen/blocks.ts +658 -0
- package/src/models/qwen/checkpoint.ts +190 -0
- package/src/models/qwen/families.ts +57 -0
- package/src/models/qwen/media-input.ts +161 -0
- package/src/models/qwen/mtp.ts +101 -0
- package/src/models/qwen/qwen3-moe.ts +355 -0
- package/src/models/qwen/qwen3.ts +323 -0
- package/src/models/qwen/qwen38-27b-trellis-m1max.ts +1532 -0
- package/src/models/qwen/qwen38-27b-trellis-m4pro.ts +1092 -0
- package/src/models/qwen/qwen3_5.ts +433 -0
- package/src/models/qwen/written-layers.ts +285 -0
- package/src/models/row-table.ts +1 -0
- package/src/models/runtime.ts +73 -0
- package/src/models/speculative/deepspec-artifact.ts +10 -0
- package/src/models/speculative/deepspec.ts +744 -0
- package/src/models/speculative/dflash.ts +553 -0
- package/src/models/speculative/dflash2-artifact.ts +9 -0
- package/src/models/speculative/dflash2-operations.ts +1 -0
- package/src/models/speculative/dflash2.ts +576 -0
- package/src/models/speculative/loader.ts +29 -0
- package/src/models/support.ts +79 -0
- package/src/models/universal/archs.ts +365 -0
- package/src/models/universal/blocks.ts +479 -0
- package/src/models/universal/dense.ts +255 -0
- package/src/models/universal/family.ts +17 -0
- package/src/models/vision/qwen3vl.ts +566 -0
- package/src/models/vision/siglip.ts +520 -0
- package/src/models/vision/unified.ts +176 -0
- package/src/models/whisper/family.ts +19 -0
- package/src/models/whisper/fast.ts +734 -0
- package/src/models/whisper/model.ts +430 -0
- package/src/runtime/compiled-trace.ts +16 -0
- package/src/runtime/config.ts +77 -0
- package/src/runtime/expert-trace.ts +71 -0
- package/src/runtime/graph-forward.ts +65 -0
- package/src/runtime/integers.ts +6 -0
- package/src/runtime/native.ts +16 -0
- package/src/runtime/resources.ts +53 -0
- package/src/runtime/row-operations.ts +42 -0
- package/src/runtime/trace.ts +161 -0
- package/src/sampling/curve.ts +122 -0
- package/src/sampling/draft.ts +55 -0
- package/src/sampling/extras.ts +45 -0
- package/src/sampling/filters.ts +134 -0
- package/src/sampling/grammar.ts +630 -0
- package/src/sampling/hlg.ts +290 -0
- package/src/sampling/index.ts +9 -0
- package/src/sampling/processors.ts +142 -0
- package/src/sampling/sampler.ts +120 -0
- package/src/sampling/step.ts +343 -0
- package/src/sampling/types.ts +161 -0
- package/src/sampling/window-filters.ts +46 -0
- package/src/scoring/decisions.ts +96 -0
- package/src/scoring/full-sequence.ts +103 -0
- package/src/scoring/index.ts +9 -0
- package/src/scoring/kl.ts +29 -0
- package/src/scoring/perplexity.ts +165 -0
- package/src/scoring/response-logps.ts +65 -0
- package/src/state/affine-attention.ts +180 -0
- package/src/state/attachment.ts +68 -0
- package/src/state/attention-read.ts +215 -0
- package/src/state/batched-kv.ts +294 -0
- package/src/state/batched-mask.ts +445 -0
- package/src/state/batched-quant.ts +389 -0
- package/src/state/batched-quantized-kv.ts +209 -0
- package/src/state/batched-rotating-quant.ts +457 -0
- package/src/state/batched-rotating-state.ts +149 -0
- package/src/state/batched-rotating.ts +430 -0
- package/src/state/batched-row-storage.ts +179 -0
- package/src/state/batched-ssm.ts +54 -0
- package/src/state/batched-turboquant-kv.ts +144 -0
- package/src/state/bf16-first-kv.ts +663 -0
- package/src/state/cache-service.ts +592 -0
- package/src/state/capabilities.ts +65 -0
- package/src/state/checkpoint.ts +46 -0
- package/src/state/continuation-persistence.ts +126 -0
- package/src/state/decode-step-inputs.ts +31 -0
- package/src/state/decoded-kv-donor.ts +12 -0
- package/src/state/delayed-quantized-kv.ts +141 -0
- package/src/state/delayed-rotating-quantized-kv.ts +178 -0
- package/src/state/delayed-turboquant-kv.ts +123 -0
- package/src/state/durability.ts +214 -0
- package/src/state/encoder-cache.ts +26 -0
- package/src/state/full-kv-row-append.ts +33 -0
- package/src/state/full-kv-row-donor.ts +54 -0
- package/src/state/full-prefill-padding.ts +56 -0
- package/src/state/full-prefill-row.ts +112 -0
- package/src/state/full-transitioning-kv-rows.ts +70 -0
- package/src/state/glm52-cache.ts +783 -0
- package/src/state/grouped-heads-kv4-cache.ts +26 -0
- package/src/state/index.ts +38 -0
- package/src/state/kv-attention-view.ts +77 -0
- package/src/state/kv-budget.ts +14 -0
- package/src/state/kv-scheme.ts +315 -0
- package/src/state/kv-storage.ts +57 -0
- package/src/state/kv-tensor-rows.ts +212 -0
- package/src/state/kv.ts +324 -0
- package/src/state/kv4-head256-cache.ts +46 -0
- package/src/state/latent-legos.ts +895 -0
- package/src/state/layout.ts +277 -0
- package/src/state/leases.ts +26 -0
- package/src/state/paged/cache.ts +689 -0
- package/src/state/paged/codec.ts +36 -0
- package/src/state/paged/rows.ts +223 -0
- package/src/state/persistence-types.ts +173 -0
- package/src/state/persistence-worker.ts +83 -0
- package/src/state/persistence.ts +1036 -0
- package/src/state/persistence.worker.d.ts +3 -0
- package/src/state/persistence.worker.js +264 -0
- package/src/state/prefix-cache.ts +685 -0
- package/src/state/quantized-kv.ts +369 -0
- package/src/state/quantized-tensor.ts +26 -0
- package/src/state/request-policy.ts +48 -0
- package/src/state/retention-policy.ts +37 -0
- package/src/state/rollback.ts +131 -0
- package/src/state/rotating-kv-donor.ts +35 -0
- package/src/state/rotating-kv-layout.ts +341 -0
- package/src/state/rotating-kv.ts +529 -0
- package/src/state/rotating-quantized-kv.ts +616 -0
- package/src/state/rotating-row-transaction.ts +35 -0
- package/src/state/rows.ts +104 -0
- package/src/state/softcap-attention.ts +118 -0
- package/src/state/speculative/glm52-mtp-state.ts +38 -0
- package/src/state/speculative/qwen-mtp-state.ts +34 -0
- package/src/state/speculative-rotating-kv.ts +113 -0
- package/src/state/ssd-cache.ts +434 -0
- package/src/state/ssm-prefill-padding.ts +39 -0
- package/src/state/ssm.ts +422 -0
- package/src/state/target-layout.ts +7 -0
- package/src/state/tiered-prefix-cache.ts +45 -0
- package/src/state/training-cache.ts +300 -0
- package/src/state/transitioning-kv-rows.ts +267 -0
- package/src/state/turboquant-attention.ts +97 -0
- package/src/state/turboquant-codec.ts +90 -0
- package/src/state/turboquant-kv.ts +442 -0
- package/src/state/views.ts +26 -0
- package/src/transcription/index.ts +3 -0
- package/src/transcription/silero-vad.ts +379 -0
- package/src/transcription/whisper/decode.ts +773 -0
- package/src/transcription/whisper/format.ts +74 -0
- package/src/transcription/whisper/index.ts +4 -0
- package/src/transcription/whisper/timing.ts +274 -0
- package/src/transcription/whisper/transcribe.ts +394 -0
- package/src/transcription/whisper/types.ts +22 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import { gateUpFactoredRow, gateUpFactoredRows } from "../kernels/trellis/gate-up-factored";
|
|
3
|
+
import { mixedGateUpFactoredRows } from "../kernels/trellis/mixed-gate-up-factored";
|
|
4
|
+
import { gateUpMma } from "../kernels/trellis/gate-up-mma";
|
|
5
|
+
import { gateUpMmaWide } from "../kernels/trellis/gate-up-mma-wide";
|
|
6
|
+
import { gateUpCodebookMma } from "../kernels/trellis/gate-up-codebook-mma";
|
|
7
|
+
import { TrellisLinear } from "./trellis-linear";
|
|
8
|
+
|
|
9
|
+
// Fused gate/up/SwiGLU layers over a Qwen MLP's axis-1 Trellis gate and up
|
|
10
|
+
// projections. Each layer runs one kernel for one row form; the owning graph
|
|
11
|
+
// picks the layer for the request's row count and the weights' bit widths.
|
|
12
|
+
|
|
13
|
+
function sameAxis1(name: string, gate: TrellisLinear, up: TrellisLinear, sameWidth: boolean): void {
|
|
14
|
+
const a = gate.geometry, b = up.geometry;
|
|
15
|
+
if (a.axis !== 1 || b.axis !== 1 || a.rows !== b.rows || a.cols !== b.cols ||
|
|
16
|
+
a.T !== b.T || a.L !== b.L || (a.k === b.k) !== sameWidth)
|
|
17
|
+
throw new Error(`${name}: gate/up need packed axis-1 codes of one geometry, ${sameWidth ? "the same" : "different"} widths`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** One activation row; gate and up at the same width. */
|
|
21
|
+
export class FactoredGateUpRow {
|
|
22
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) { sameAxis1("FactoredGateUpRow", gate, up, true); }
|
|
23
|
+
forward(x: MlxArray): MlxArray { return gateUpFactoredRow(x, this.gate, this.up); }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 2..4 activation rows sharing each decoded weight; same widths. */
|
|
27
|
+
export class FactoredGateUpRows {
|
|
28
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) { sameAxis1("FactoredGateUpRows", gate, up, true); }
|
|
29
|
+
forward(x: MlxArray): MlxArray { return gateUpFactoredRows(x, this.gate, this.up); }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 1..4 activation rows; gate and up at different widths. */
|
|
33
|
+
export class FactoredMixedGateUp {
|
|
34
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) { sameAxis1("FactoredMixedGateUp", gate, up, false); }
|
|
35
|
+
forward(x: MlxArray): MlxArray { return mixedGateUpFactoredRows(x, this.gate, this.up); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 1..8 activation rows on the simdgroup matrix unit; widths may differ. */
|
|
39
|
+
export class MmaGateUp {
|
|
40
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) {
|
|
41
|
+
const a = gate.geometry, b = up.geometry;
|
|
42
|
+
if (a.axis !== 1 || b.axis !== 1 || a.rows !== b.rows || a.cols !== b.cols ||
|
|
43
|
+
a.T !== b.T || a.L !== b.L || a.rows % 8 !== 0 || a.cols % 256 !== 0)
|
|
44
|
+
throw new Error("MmaGateUp: gate/up need packed axis-1 codes of one geometry (rows % 8, cols % 256)");
|
|
45
|
+
}
|
|
46
|
+
forward(x: MlxArray): MlxArray { return gateUpMma(x, this.gate, this.up); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Any number of activation rows on the simdgroup matrix unit, each decoded weight fragment feeding several 8-row
|
|
50
|
+
* blocks (a threadgroup codebook, activations staged in threadgroup memory); widths may differ. */
|
|
51
|
+
export class MmaGateUpWide {
|
|
52
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) {
|
|
53
|
+
const a = gate.geometry, b = up.geometry;
|
|
54
|
+
if (a.axis !== 1 || b.axis !== 1 || a.rows !== b.rows || a.cols !== b.cols ||
|
|
55
|
+
a.T !== b.T || a.L !== b.L || a.rows % 8 !== 0 || a.cols % 256 !== 0 || a.L > 12)
|
|
56
|
+
throw new Error("MmaGateUpWide: gate/up need packed axis-1 codes of one geometry (rows % 8, cols % 256, L <= 12)");
|
|
57
|
+
}
|
|
58
|
+
forward(x: MlxArray): MlxArray { return gateUpMmaWide(x, this.gate, this.up); }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 1..8 activation rows on the simdgroup matrix unit, each code value read from a threadgroup codebook (MmaGateUp's
|
|
62
|
+
* bits); widths may differ. */
|
|
63
|
+
export class CodebookMmaGateUp {
|
|
64
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) {
|
|
65
|
+
const a = gate.geometry, b = up.geometry;
|
|
66
|
+
if (a.axis !== 1 || b.axis !== 1 || a.rows !== b.rows || a.cols !== b.cols ||
|
|
67
|
+
a.T !== b.T || a.L !== b.L || a.rows % 8 !== 0 || a.cols % 256 !== 0 || a.L > 12)
|
|
68
|
+
throw new Error("CodebookMmaGateUp: gate/up need packed axis-1 codes of one geometry (rows % 8, cols % 256, L <= 12)");
|
|
69
|
+
}
|
|
70
|
+
forward(x: MlxArray): MlxArray { return gateUpCodebookMma(x, this.gate, this.up); }
|
|
71
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import { Dtype, deviceArchitecture } from "@mlx-bun/mlx/ffi";
|
|
3
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
4
|
+
import { quantFor, type ModelConfig, type QuantSpec } from "../artifacts/config";
|
|
5
|
+
import type { Weights } from "../artifacts/weights";
|
|
6
|
+
import { LoraDelta, type LoraState, type LoraWeights } from "./lora";
|
|
7
|
+
import { QuantizedLinear } from "./quantized-linear";
|
|
8
|
+
import { wordsPerBlock } from "../kernels/trellis/codebook";
|
|
9
|
+
import {
|
|
10
|
+
TRELLIS_MATVEC_MAX_M as MATVEC_MAX_M, type TrellisGeometry,
|
|
11
|
+
expandTrellis, trellisReduce, trellisScatter, fusedGateUpSwiglu as gateUpKernel,
|
|
12
|
+
tiledTrellisPrefill, tiledTrellisPrefillEligible,
|
|
13
|
+
splitKTrellisPrefill, splitKTrellisPrefillEligible,
|
|
14
|
+
wideTrellisPrefill, wideTrellisPrefillEligible,
|
|
15
|
+
qualifiedRepresentations, type TrellisRepresentations,
|
|
16
|
+
} from "../kernels/trellis/index";
|
|
17
|
+
export { TRELLIS_MATVEC_MAX_M, type TrellisGeometry, type MixedGateUpTail } from "../kernels/trellis/index";
|
|
18
|
+
|
|
19
|
+
/** The decode variant these layers pass the kernels (semantics in
|
|
20
|
+
* kernels/trellis/index.ts): 13, which passed the M4 Pro closeout matrix.
|
|
21
|
+
* Benches and tests that compare variants call the kernels directly. */
|
|
22
|
+
const VARIANT = 13;
|
|
23
|
+
|
|
24
|
+
export function trellisGeometry(codes: MlxArray, spec: QuantSpec): TrellisGeometry {
|
|
25
|
+
const tr = spec.trellis;
|
|
26
|
+
if (spec.mode !== "trellis" || !tr) throw new Error("trellisGeometry: spec is not a trellis spec");
|
|
27
|
+
const k = spec.bits, T = spec.groupSize;
|
|
28
|
+
if (codes.ndim === 3) {
|
|
29
|
+
const [groups, rows, words] = codes.shape as [number, number, number];
|
|
30
|
+
if (codes.dtype !== Dtype.uint32 || tr.axis !== 0 || k !== 3 || T !== 256 ||
|
|
31
|
+
tr.L !== 12 || tr.code !== "1mad" || words !== 48 || groups < 1 || rows < 1)
|
|
32
|
+
throw new Error("trellis: unsupported interleaved code shape or quantization");
|
|
33
|
+
const cols = groups * 512;
|
|
34
|
+
return { k, L: tr.L, T, axis: 0, rows, cols, inFeatures: rows,
|
|
35
|
+
outFeatures: cols, blockInterleave: 2 };
|
|
36
|
+
}
|
|
37
|
+
if (codes.ndim !== 2) throw new Error("trellis: expected a 2D or interleaved 3D code tensor");
|
|
38
|
+
const [rows, words] = codes.shape as [number, number];
|
|
39
|
+
if ((words * 32) % k !== 0) throw new Error(`trellis: ${words} words not a whole number of ${k}-bit symbols`);
|
|
40
|
+
const cols = (words * 32) / k;
|
|
41
|
+
if (cols % T !== 0) throw new Error(`trellis: ${cols} coded columns not a multiple of block ${T}`);
|
|
42
|
+
wordsPerBlock(T, k);
|
|
43
|
+
return {
|
|
44
|
+
k, L: tr.L, T, axis: tr.axis, rows, cols,
|
|
45
|
+
inFeatures: tr.axis === 1 ? cols : rows,
|
|
46
|
+
outFeatures: tr.axis === 1 ? rows : cols,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Can gate and up be served by the fused kernel? Same axis-1 geometry and k. */
|
|
51
|
+
export function fusedGateUpEligible(gate: TrellisLinear, up: TrellisLinear): boolean {
|
|
52
|
+
const a = gate.geometry, b = up.geometry;
|
|
53
|
+
return a.axis === 1 && b.axis === 1 &&
|
|
54
|
+
a.k === b.k && a.rows === b.rows && a.cols === b.cols && a.T === b.T && a.L === b.L;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Borrow the layers' weights; the gate's stored representations choose the decode. */
|
|
58
|
+
export function fusedGateUpSwiglu(x: MlxArray, gate: TrellisLinear, up: TrellisLinear): MlxArray {
|
|
59
|
+
return gateUpKernel(x, gate, up, VARIANT, gate.representations.gateUpCodebook);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Output rows [start, stop) of an axis-1 (row-coded) Trellis projection as an
|
|
63
|
+
* owned projection: each output row has its own code row and scale, so the
|
|
64
|
+
* slice decodes exactly the same values. A leading-axis range of row-major
|
|
65
|
+
* codes is already row-contiguous, so these are views of the parent's memory. */
|
|
66
|
+
export function trellisRows(lin: TrellisLinear, start: number, stop: number): TrellisLinear {
|
|
67
|
+
const g = lin.geometry;
|
|
68
|
+
if (g.axis !== 1 || g.blockInterleave || lin.codes.ndim !== 2) throw new Error("trellisRows: needs row-coded (axis 1) codes");
|
|
69
|
+
const codes = lin.codes.slice([start, 0], [stop, lin.codes.shape[1]!]);
|
|
70
|
+
const scales = lin.scales.slice([start], [stop]);
|
|
71
|
+
ops.evalAll([codes, scales]);
|
|
72
|
+
return new TrellisLinear(codes, scales, lin.spec, false, lin.representations);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Stored rows [start, stop) of an axis-0 Trellis projection (input features of
|
|
76
|
+
* a down projection): 2D codes slice to a view; block-interleaved 3D codes
|
|
77
|
+
* [groups, rows, words] slice their middle axis (kernels copy that view). */
|
|
78
|
+
export function trellisStoredRows(lin: TrellisLinear, start: number, stop: number): TrellisLinear {
|
|
79
|
+
const g = lin.geometry;
|
|
80
|
+
if (g.axis !== 0) throw new Error("trellisStoredRows: needs axis-0 codes");
|
|
81
|
+
const codes = lin.codes.ndim === 3
|
|
82
|
+
? lin.codes.slice([0, start, 0], [lin.codes.shape[0]!, stop, lin.codes.shape[2]!])
|
|
83
|
+
: lin.codes.slice([start, 0], [stop, lin.codes.shape[1]!]);
|
|
84
|
+
const scales = lin.scales.slice([start], [stop]);
|
|
85
|
+
ops.evalAll([codes, scales]);
|
|
86
|
+
return new TrellisLinear(codes, scales, lin.spec, false, lin.representations);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class TrellisLinear {
|
|
90
|
+
readonly delta: LoraDelta;
|
|
91
|
+
get adapters(): Map<string, LoraWeights> | null { return this.delta.adapters; }
|
|
92
|
+
set adapters(value: Map<string, LoraWeights> | null) { this.delta.adapters = value; }
|
|
93
|
+
get loraState(): LoraState | null { return this.delta.loraState; }
|
|
94
|
+
set loraState(value: LoraState | null) { this.delta.loraState = value; }
|
|
95
|
+
get dropoutId(): number { return this.delta.dropoutId; }
|
|
96
|
+
set dropoutId(value: number) { this.delta.dropoutId = value; }
|
|
97
|
+
readonly geometry: TrellisGeometry;
|
|
98
|
+
readonly spec: QuantSpec;
|
|
99
|
+
/** The decode variant every kernel over these weights runs (kernels/trellis/index.ts): the row-form layers
|
|
100
|
+
* built on this projection take it at their construction. */
|
|
101
|
+
readonly variant = VARIANT;
|
|
102
|
+
/** What the kernels take from this machine's GPU family (single-row
|
|
103
|
+
* representations, wide prefill), fixed here and passed on every call. */
|
|
104
|
+
readonly representations: TrellisRepresentations;
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
readonly codes: MlxArray,
|
|
108
|
+
readonly scales: MlxArray,
|
|
109
|
+
spec: QuantSpec,
|
|
110
|
+
readonly useSharedScatterCodebook = false,
|
|
111
|
+
representations: TrellisRepresentations = qualifiedRepresentations(deviceArchitecture()),
|
|
112
|
+
) {
|
|
113
|
+
this.spec = spec;
|
|
114
|
+
this.geometry = trellisGeometry(codes, spec);
|
|
115
|
+
this.delta = new LoraDelta(this.geometry.inFeatures, this.geometry.outFeatures);
|
|
116
|
+
if (this.geometry.blockInterleave && (scales.ndim !== 1 || scales.size !== this.geometry.rows))
|
|
117
|
+
throw new Error("trellis: interleaved codes require one scale per stored row");
|
|
118
|
+
this.representations = representations;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static load(weights: Weights, path: string, config: ModelConfig, useSharedScatterCodebook = false, representations?: TrellisRepresentations): TrellisLinear {
|
|
122
|
+
const spec = quantFor(config.quantization, path);
|
|
123
|
+
if (!spec || spec.mode !== "trellis")
|
|
124
|
+
throw new Error(`${path}: expected a trellis quant spec`);
|
|
125
|
+
if (!weights.has(`${path}.scales`)) throw new Error(`${path}: trellis tensor has no .scales`);
|
|
126
|
+
return new TrellisLinear(weights.tensor(`${path}.weight`), weights.tensor(`${path}.scales`), spec, useSharedScatterCodebook, representations);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static isTrellis(config: ModelConfig, path: string): boolean {
|
|
130
|
+
return quantFor(config.quantization, path)?.mode === "trellis";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get inFeatures(): number { return this.geometry.inFeatures; }
|
|
134
|
+
get outFeatures(): number { return this.geometry.outFeatures; }
|
|
135
|
+
|
|
136
|
+
/** The weight as [out, in] bf16 (decoded; transposed for axis=0). */
|
|
137
|
+
expandWeight(dtype: Dtype = Dtype.bfloat16): MlxArray {
|
|
138
|
+
const g = this.geometry;
|
|
139
|
+
const stored = expandTrellis(this.codes, this.scales, g, dtype, VARIANT);
|
|
140
|
+
if (g.axis === 1) return stored;
|
|
141
|
+
const t = ops.transposeAxes(stored, [1, 0]);
|
|
142
|
+
const w = ops.contiguous(t);
|
|
143
|
+
t.dispose(); stored.dispose();
|
|
144
|
+
return w;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A caller may prove row-contiguous, aligned input from an allocating op
|
|
148
|
+
* such as RMSNorm. Lazy array strides cannot establish that before eval.
|
|
149
|
+
* Rows past the tiled kernels expand the dense weight and multiply; that
|
|
150
|
+
* product is evaluated before returning, so building the next projection
|
|
151
|
+
* never holds a second expansion, unless `boundExpansion` is false: a caller
|
|
152
|
+
* that queues several projections at once (the Neural Engine split's GPU
|
|
153
|
+
* share) holds their expansions until it evaluates them. */
|
|
154
|
+
forward(x: MlxArray, inputRowContiguous = false, boundExpansion = true): MlxArray {
|
|
155
|
+
const g = this.geometry;
|
|
156
|
+
const lead = x.shape.slice(0, -1);
|
|
157
|
+
const M = lead.reduce((a, b) => a * b, 1);
|
|
158
|
+
if (x.shape[x.shape.length - 1] !== g.inFeatures)
|
|
159
|
+
throw new Error(`TrellisLinear: input dim ${x.shape[x.shape.length - 1]} != ${g.inFeatures}`);
|
|
160
|
+
const x2 = ops.reshape(x, [M, g.inFeatures]);
|
|
161
|
+
let y: MlxArray;
|
|
162
|
+
let expandedWeights = false;
|
|
163
|
+
if (M <= MATVEC_MAX_M) y = g.axis === 1 ? trellisReduce(x2, this.codes, this.scales, g, VARIANT) : trellisScatter(x2, this.codes, this.scales, g, VARIANT, this.useSharedScatterCodebook, this.representations.scatter);
|
|
164
|
+
else if (inputRowContiguous && wideTrellisPrefillEligible(g, M, x.dtype, this.representations.widePrefill))
|
|
165
|
+
y = wideTrellisPrefill(x2, this.codes, this.scales, g);
|
|
166
|
+
else if (tiledTrellisPrefillEligible(g, M, x.dtype, this.representations.widePrefill))
|
|
167
|
+
y = tiledTrellisPrefill(x2, this.codes, this.scales, g);
|
|
168
|
+
else if (splitKTrellisPrefillEligible(g, M, x.dtype))
|
|
169
|
+
y = splitKTrellisPrefill(x2, this.codes, this.scales, g);
|
|
170
|
+
else {
|
|
171
|
+
expandedWeights = true;
|
|
172
|
+
const stored = expandTrellis(this.codes, this.scales, g, x.dtype, VARIANT);
|
|
173
|
+
if (g.axis === 1) {
|
|
174
|
+
const wt = ops.transposeAxes(stored, [1, 0]);
|
|
175
|
+
y = ops.matmul(x2, wt);
|
|
176
|
+
wt.dispose();
|
|
177
|
+
} else y = ops.matmul(x2, stored);
|
|
178
|
+
stored.dispose();
|
|
179
|
+
}
|
|
180
|
+
x2.dispose();
|
|
181
|
+
const out = ops.reshape(y, [...lead, g.outFeatures]);
|
|
182
|
+
y.dispose();
|
|
183
|
+
// Bound dense weight expansions before building the next projection.
|
|
184
|
+
// Disposing `stored` above releases only its JS handle, not the lazy
|
|
185
|
+
// matmul's reference. Direct tiles and packed matvecs stay lazy: they
|
|
186
|
+
// hold activations and bounded partials, without a dense weight matrix.
|
|
187
|
+
if (expandedWeights && boundExpansion) out.eval();
|
|
188
|
+
return this.delta.forward(x, out);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Packed Trellis weights decoded once, at construction, into 8-bit g64 affine
|
|
193
|
+
* and served through the stock QuantizedLinear (the eval-carrier numerics,
|
|
194
|
+
* about -45 dB against the packed kernels): for a machine where the packed
|
|
195
|
+
* kernels lose. Built explicitly by whoever selects it; holds no packed codes. */
|
|
196
|
+
export class ExpandedTrellisLinear {
|
|
197
|
+
readonly carrier: QuantizedLinear;
|
|
198
|
+
|
|
199
|
+
constructor(packed: TrellisLinear) {
|
|
200
|
+
const w = packed.expandWeight(Dtype.bfloat16);
|
|
201
|
+
const q = ops.quantize(w, 64, 8, "affine");
|
|
202
|
+
ops.evalAll([q.packed, q.scales, ...(q.biases ? [q.biases] : [])]);
|
|
203
|
+
w.dispose();
|
|
204
|
+
this.carrier = new QuantizedLinear(q.packed, q.scales, q.biases, { bits: 8, groupSize: 64, mode: "affine" });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
get inFeatures(): number { return this.carrier.inFeatures; }
|
|
208
|
+
get outFeatures(): number { return this.carrier.outFeatures; }
|
|
209
|
+
|
|
210
|
+
forward(x: MlxArray): MlxArray { return this.carrier.forward(x); }
|
|
211
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { expandTrellis, trellisReduce, trellisScatter, wideTrellisPrefill, tiledTrellisPrefill, splitKTrellisPrefill } from "../kernels/trellis/index";
|
|
4
|
+
import { LoraOperation, LoraState } from "./lora";
|
|
5
|
+
import { compiledSwiglu } from "./swiglu";
|
|
6
|
+
import { fusedGateUpSwiglu, type TrellisLinear } from "./trellis-linear";
|
|
7
|
+
|
|
8
|
+
export interface RowProjection { forward(x: MlxArray): MlxArray }
|
|
9
|
+
|
|
10
|
+
/** The graph supplies a projection kernel. Shapes preserve the input's leading
|
|
11
|
+
* dimensions; reshaped handles are released while the result remains lazy. */
|
|
12
|
+
function project(lin: TrellisLinear, x: MlxArray, kernel: (x: MlxArray) => MlxArray): MlxArray {
|
|
13
|
+
const lead = x.shape.slice(0, -1);
|
|
14
|
+
using rows = ops.reshape(x, [lead.reduce((a, b) => a * b, 1), lin.inFeatures]);
|
|
15
|
+
using out = kernel(rows);
|
|
16
|
+
return lin.delta.forward(x, ops.reshape(out, [...lead, lin.outFeatures]));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class TrellisReducePrompt {
|
|
20
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
21
|
+
forward(x: MlxArray): MlxArray {
|
|
22
|
+
return project(this.linear, x, rows => trellisReduce(rows, this.linear.codes, this.linear.scales, this.linear.geometry, this.linear.variant));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class TrellisScatterPrompt {
|
|
26
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
27
|
+
forward(x: MlxArray): MlxArray {
|
|
28
|
+
return project(this.linear, x, rows => trellisScatter(rows, this.linear.codes, this.linear.scales, this.linear.geometry, this.linear.variant, this.linear.useSharedScatterCodebook, this.linear.representations.scatter));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class TrellisWidePrompt {
|
|
32
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
33
|
+
forward(x: MlxArray): MlxArray {
|
|
34
|
+
return project(this.linear, x, rows => wideTrellisPrefill(rows, this.linear.codes, this.linear.scales, this.linear.geometry));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class TrellisTiledPrompt {
|
|
38
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
39
|
+
forward(x: MlxArray): MlxArray {
|
|
40
|
+
return project(this.linear, x, rows => tiledTrellisPrefill(rows, this.linear.codes, this.linear.scales, this.linear.geometry));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class TrellisSplitKPrompt {
|
|
44
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
45
|
+
forward(x: MlxArray): MlxArray {
|
|
46
|
+
return project(this.linear, x, rows => splitKTrellisPrefill(rows, this.linear.codes, this.linear.scales, this.linear.geometry));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Axis-1 dense expansion; evaluate the matmul before the next projection so
|
|
51
|
+
* the deferred graph does not retain every expanded weight. */
|
|
52
|
+
export class TrellisExpandReducePrompt {
|
|
53
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
54
|
+
forward(x: MlxArray): MlxArray {
|
|
55
|
+
const out = project(this.linear, x, rows => {
|
|
56
|
+
using stored = expandTrellis(this.linear.codes, this.linear.scales, this.linear.geometry, x.dtype, this.linear.variant);
|
|
57
|
+
using wt = ops.transposeAxes(stored, [1, 0]);
|
|
58
|
+
return ops.matmul(rows, wt);
|
|
59
|
+
});
|
|
60
|
+
out.eval();
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export class TrellisExpandScatterPrompt {
|
|
65
|
+
constructor(readonly linear: TrellisLinear) {}
|
|
66
|
+
forward(x: MlxArray): MlxArray {
|
|
67
|
+
const out = project(this.linear, x, rows => {
|
|
68
|
+
using stored = expandTrellis(this.linear.codes, this.linear.scales, this.linear.geometry, x.dtype, this.linear.variant);
|
|
69
|
+
return ops.matmul(rows, stored);
|
|
70
|
+
});
|
|
71
|
+
out.eval();
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Fused gate/up/SwiGLU followed by the literal down projection. */
|
|
77
|
+
export class TrellisFusedMLP {
|
|
78
|
+
readonly #operation: LoraOperation;
|
|
79
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear, readonly down: RowProjection,
|
|
80
|
+
state?: LoraState) {
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
const adapted = new TrellisUnfusedMLP(new TrellisReducePrompt(gate), new TrellisReducePrompt(up), down);
|
|
84
|
+
this.#operation = new LoraOperation(x => {
|
|
85
|
+
using hidden = fusedGateUpSwiglu(x, gate, up);
|
|
86
|
+
return down.forward(hidden);
|
|
87
|
+
}, x => adapted.forward(x), state, [gate.delta, up.delta]);
|
|
88
|
+
}
|
|
89
|
+
forward(x: MlxArray): MlxArray { return this.#operation.forward(x); }
|
|
90
|
+
dispose(): void { this.#operation.dispose(); }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Independent projections followed by the oracle's compiled SwiGLU. */
|
|
94
|
+
export class TrellisUnfusedMLP {
|
|
95
|
+
constructor(readonly gate: RowProjection, readonly up: RowProjection, readonly down: RowProjection) {}
|
|
96
|
+
forward(x: MlxArray): MlxArray {
|
|
97
|
+
using gate = this.gate.forward(x);
|
|
98
|
+
using up = this.up.forward(x);
|
|
99
|
+
using hidden = compiledSwiglu(gate, up);
|
|
100
|
+
return this.down.forward(hidden);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import { gateUpReduceRow, gateUpReduceRows } from "../kernels/trellis/gate-up";
|
|
3
|
+
import { fusedGateUpSwigluMixed, type MixedGateUpTail } from "../kernels/trellis/mixed-gate-up";
|
|
4
|
+
import type { TrellisLinear } from "./trellis-linear";
|
|
5
|
+
|
|
6
|
+
// Fused gate/up/SwiGLU layers over a Qwen MLP's axis-1 Trellis gate and up
|
|
7
|
+
// projections on the reduce kernels (one SIMD group per output row, the decoded
|
|
8
|
+
// weight times the scale per weight; kernels/trellis/index.ts). Each layer runs
|
|
9
|
+
// one kernel for one row form and takes everything that kernel varies on at
|
|
10
|
+
// construction: the projections' decode variant and the representations their
|
|
11
|
+
// GPU family qualifies (`TrellisLinear.representations`). The owning graph picks
|
|
12
|
+
// the layer for the rows of its width class and the weights' bit widths.
|
|
13
|
+
|
|
14
|
+
function sameAxis1(name: string, gate: TrellisLinear, up: TrellisLinear, sameWidth: boolean): void {
|
|
15
|
+
const a = gate.geometry, b = up.geometry;
|
|
16
|
+
if (a.axis !== 1 || b.axis !== 1 || a.rows !== b.rows || a.cols !== b.cols ||
|
|
17
|
+
a.T !== b.T || a.L !== b.L || (a.k === b.k) !== sameWidth)
|
|
18
|
+
throw new Error(`${name}: gate/up need packed axis-1 codes of one geometry, ${sameWidth ? "the same" : "different"} widths`);
|
|
19
|
+
if (gate.variant !== up.variant) throw new Error(`${name}: gate and up decode with different variants`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** One activation row; gate and up at the same width. The threadgroup codebook decode where the gate's GPU family
|
|
23
|
+
* qualifies it (applied by the kernel at the measured artifact shape). */
|
|
24
|
+
export class ReduceGateUpRow {
|
|
25
|
+
readonly #variant: number;
|
|
26
|
+
readonly #codebook: boolean;
|
|
27
|
+
|
|
28
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) {
|
|
29
|
+
sameAxis1("ReduceGateUpRow", gate, up, true);
|
|
30
|
+
this.#variant = gate.variant;
|
|
31
|
+
this.#codebook = gate.representations.gateUpCodebook;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** `x` [.., in] (borrowed, one row) to an owned [.., rows]. */
|
|
35
|
+
forward(x: MlxArray): MlxArray { return gateUpReduceRow(x, this.gate, this.up, this.#variant, this.#codebook); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 2..4 activation rows sharing each decoded weight; gate and up at the same width. */
|
|
39
|
+
export class ReduceGateUpRows {
|
|
40
|
+
readonly #variant: number;
|
|
41
|
+
|
|
42
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear) {
|
|
43
|
+
sameAxis1("ReduceGateUpRows", gate, up, true);
|
|
44
|
+
this.#variant = gate.variant;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** `x` [.., in] (borrowed, 2..4 rows) to an owned [.., rows]. */
|
|
48
|
+
forward(x: MlxArray): MlxArray { return gateUpReduceRows(x, this.gate, this.up, this.#variant); }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 1..4 activation rows; gate and up at different widths. `tail` is the SwiGLU arithmetic the kernel reproduces
|
|
52
|
+
* (`"split"`: MLX's compiled swiglu over the two bf16 projections, bit for bit; `"fused"`: the float32 sigmoid of the
|
|
53
|
+
* same-width kernel). The exact-bits decoder at one row where the gate's GPU family qualifies it. */
|
|
54
|
+
export class ReduceMixedGateUp {
|
|
55
|
+
readonly #variant: number;
|
|
56
|
+
readonly #exactBits: boolean;
|
|
57
|
+
|
|
58
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear, readonly tail: MixedGateUpTail) {
|
|
59
|
+
sameAxis1("ReduceMixedGateUp", gate, up, false);
|
|
60
|
+
this.#variant = gate.variant;
|
|
61
|
+
this.#exactBits = gate.representations.mixedExactBits;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** `x` [.., in] (borrowed, 1..4 rows) to an owned [.., rows]. */
|
|
65
|
+
forward(x: MlxArray): MlxArray {
|
|
66
|
+
return fusedGateUpSwigluMixed(x, this.gate, this.up, this.#variant, this.tail, this.#exactBits);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Fixed split activation for the written M1 graph's mixed-width projections. */
|
|
71
|
+
export class ReduceSplitMixedGateUp extends ReduceMixedGateUp {
|
|
72
|
+
constructor(gate: TrellisLinear, up: TrellisLinear) { super(gate, up, "split"); }
|
|
73
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { balancedScatterRow, balancedScatterRows, scatterRow, scatterRows } from "../kernels/trellis/scatter";
|
|
4
|
+
import type { ScatterRepresentation } from "../kernels/trellis/qualified";
|
|
5
|
+
import type { TrellisLinear } from "./trellis-linear";
|
|
6
|
+
|
|
7
|
+
// Packed down-projection layers over a Qwen MLP's axis-0 Trellis codes on the
|
|
8
|
+
// scatter kernels (lane per code word, split-K partials folded by one sum;
|
|
9
|
+
// kernels/trellis/index.ts). Each layer runs one kernel for one row form and one
|
|
10
|
+
// code layout (row-major 2- or 4-bit codes, or the 3-bit block interleave, which
|
|
11
|
+
// the balanced kernels decode) and takes everything that kernel varies on at
|
|
12
|
+
// construction: the projection's decode variant, the single-row representations
|
|
13
|
+
// its GPU family qualifies and whether its shared-row codebook was composed
|
|
14
|
+
// (`TrellisLinear.useSharedScatterCodebook`). forward(x) keeps x's leading dimensions.
|
|
15
|
+
|
|
16
|
+
function project(lin: TrellisLinear, x: MlxArray, kernel: (x2: MlxArray) => MlxArray): MlxArray {
|
|
17
|
+
const g = lin.geometry, lead = x.shape.slice(0, -1);
|
|
18
|
+
using x2 = ops.reshape(x, [lead.reduce((a, b) => a * b, 1), g.inFeatures]);
|
|
19
|
+
using y = kernel(x2);
|
|
20
|
+
return lin.delta.forward(x, ops.reshape(y, [...lead, g.outFeatures]));
|
|
21
|
+
}
|
|
22
|
+
function rowMajor(name: string, lin: TrellisLinear): void {
|
|
23
|
+
const g = lin.geometry;
|
|
24
|
+
if (g.axis !== 0 || g.blockInterleave || lin.codes.ndim !== 2 || (g.k === 3 && g.T === 256 && g.L <= 12))
|
|
25
|
+
throw new Error(`${name}: needs packed row-major axis-0 codes the balanced kernels do not decode (3-bit codes take the K3i layers)`);
|
|
26
|
+
}
|
|
27
|
+
function k3Interleaved(name: string, lin: TrellisLinear): void {
|
|
28
|
+
const g = lin.geometry;
|
|
29
|
+
if (g.axis !== 0 || g.blockInterleave !== 2 || g.k !== 3 || g.T !== 256 || g.L !== 12)
|
|
30
|
+
throw new Error(`${name}: needs 3-bit block-interleaved axis-0 codes`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One activation row; row-major codes. */
|
|
34
|
+
export class ScatterDownRow {
|
|
35
|
+
readonly #variant: number;
|
|
36
|
+
readonly #representation: ScatterRepresentation;
|
|
37
|
+
|
|
38
|
+
constructor(readonly down: TrellisLinear) {
|
|
39
|
+
rowMajor("ScatterDownRow", down);
|
|
40
|
+
this.#variant = down.variant;
|
|
41
|
+
this.#representation = down.representations.scatter;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
forward(x: MlxArray): MlxArray {
|
|
45
|
+
const { codes, scales, geometry } = this.down;
|
|
46
|
+
return project(this.down, x, x2 => scatterRow(x2, codes, scales, geometry, this.#variant, this.#representation));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 2..4 activation rows sharing each decoded weight; row-major codes. */
|
|
51
|
+
export class ScatterDownRows {
|
|
52
|
+
readonly #variant: number;
|
|
53
|
+
|
|
54
|
+
constructor(readonly down: TrellisLinear) {
|
|
55
|
+
rowMajor("ScatterDownRows", down);
|
|
56
|
+
this.#variant = down.variant;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
forward(x: MlxArray): MlxArray {
|
|
60
|
+
const { codes, scales, geometry } = this.down;
|
|
61
|
+
return project(this.down, x, x2 => scatterRows(x2, codes, scales, geometry, this.#variant));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** One activation row; 3-bit block-interleaved codes. */
|
|
66
|
+
export class ScatterDownK3iRow {
|
|
67
|
+
readonly #variant: number;
|
|
68
|
+
readonly #representation: ScatterRepresentation;
|
|
69
|
+
|
|
70
|
+
constructor(readonly down: TrellisLinear) {
|
|
71
|
+
k3Interleaved("ScatterDownK3iRow", down);
|
|
72
|
+
this.#variant = down.variant;
|
|
73
|
+
this.#representation = down.representations.scatter;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
forward(x: MlxArray): MlxArray {
|
|
77
|
+
const { codes, scales, geometry } = this.down;
|
|
78
|
+
return project(this.down, x, x2 => balancedScatterRow(x2, codes, scales, geometry, this.#variant, this.#representation));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 2..4 activation rows sharing each decoded weight; 3-bit block-interleaved codes, with the shared-row codebook at
|
|
83
|
+
* three and four rows when the projection composed it. */
|
|
84
|
+
export class ScatterDownK3iRows {
|
|
85
|
+
readonly #variant: number;
|
|
86
|
+
readonly #sharedCodebook: boolean;
|
|
87
|
+
|
|
88
|
+
constructor(readonly down: TrellisLinear) {
|
|
89
|
+
k3Interleaved("ScatterDownK3iRows", down);
|
|
90
|
+
this.#variant = down.variant;
|
|
91
|
+
this.#sharedCodebook = down.useSharedScatterCodebook;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
forward(x: MlxArray): MlxArray {
|
|
95
|
+
const { codes, scales, geometry } = this.down;
|
|
96
|
+
return project(this.down, x, x2 => balancedScatterRows(x2, codes, scales, geometry, this.#variant, this.#sharedCodebook));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// The install step for the Neural Engine: every program the shipped compositions
|
|
2
|
+
// load on this machine, compiled once into the system compile cache, the way
|
|
3
|
+
// Apple's own pipeline compiles a model for the Neural Engine on the device and
|
|
4
|
+
// keeps it. A graph never compiles at load (kernels/ane/linear); it loads what
|
|
5
|
+
// this step left in the cache, about 4 ms a program.
|
|
6
|
+
//
|
|
7
|
+
// The table is the loader's: each graph table entry whose served device is this
|
|
8
|
+
// machine's (or any) names the programs it loads for a composition
|
|
9
|
+
// (`GraphEntry.anePrograms`), a function of the composition alone, so the step
|
|
10
|
+
// needs no downloaded model. Programs identical in MIL and weights are one
|
|
11
|
+
// program and compile once. Compiling loads nothing and holds none of the ANE
|
|
12
|
+
// driver's clients. The cache is per code-signing identity, so the step runs
|
|
13
|
+
// under the executable that serves (the app's `compile-ane` verb).
|
|
14
|
+
import type { Composition } from "../contracts/portable/composition";
|
|
15
|
+
import { AneProgram, aneProgramKey, type AneCompileStatus, type AneProgramSpec } from "../kernels/ane/linear";
|
|
16
|
+
import { machineComposition } from "./composition";
|
|
17
|
+
import { MLX_GRAPH_TABLES } from "./factory";
|
|
18
|
+
import type { GraphTable } from "./implementation";
|
|
19
|
+
|
|
20
|
+
/** One distinct program of the install step and the graph entries that load it. */
|
|
21
|
+
export interface AneInstallProgram {
|
|
22
|
+
readonly spec: AneProgramSpec;
|
|
23
|
+
readonly graphs: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** An install step program and what compiling it did. */
|
|
27
|
+
export interface AneInstallResult extends AneInstallProgram {
|
|
28
|
+
readonly status: AneCompileStatus;
|
|
29
|
+
readonly milliseconds: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AneInstallOptions {
|
|
33
|
+
/** The record compiled for. Default: {@link machineComposition}, what a load with no flags resolves here. */
|
|
34
|
+
readonly composition?: Composition;
|
|
35
|
+
/** The graph tables read. Default: the loader's ({@link MLX_GRAPH_TABLES}). */
|
|
36
|
+
readonly tables?: Readonly<Record<string, GraphTable<unknown, unknown>>>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Every distinct Neural Engine program the entries of `tables` load for `composition`, among the entries whose
|
|
40
|
+
* served device is `composition.device` or unconstrained, in table order. */
|
|
41
|
+
export function aneInstallPrograms(options: AneInstallOptions = {}): AneInstallProgram[] {
|
|
42
|
+
const composition = options.composition ?? machineComposition();
|
|
43
|
+
const programs = new Map<string, { spec: AneProgramSpec; graphs: string[] }>();
|
|
44
|
+
for (const table of Object.values(options.tables ?? MLX_GRAPH_TABLES))
|
|
45
|
+
for (const entry of table.entries) {
|
|
46
|
+
if (!entry.anePrograms || (entry.serves.device !== undefined && entry.serves.device !== composition.device)) continue;
|
|
47
|
+
for (const spec of entry.anePrograms()) {
|
|
48
|
+
const key = aneProgramKey(spec), known = programs.get(key);
|
|
49
|
+
if (known) { if (!known.graphs.includes(entry.id)) known.graphs.push(entry.id); }
|
|
50
|
+
else programs.set(key, { spec, graphs: [entry.id] });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...programs.values()];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Each program {@link aneInstallPrograms} lists and whether this Mac's Neural Engine compile cache holds it for
|
|
57
|
+
* this process (the framework's `compiledModelExists`). Compiles nothing. */
|
|
58
|
+
export function aneInstallStatus(options: AneInstallOptions = {}): (AneInstallProgram & { readonly cached: boolean })[] {
|
|
59
|
+
return aneInstallPrograms(options).map(program => ({ ...program, cached: AneProgram.cached(program.spec) }));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The install step: compiles every program {@link aneInstallPrograms} lists into this Mac's Neural Engine
|
|
63
|
+
* compile cache, skipping those already there, and returns what it did for each, in order (`onProgram` hears each
|
|
64
|
+
* as it finishes). Throws `AneBridgeError` when the bridge does not load or the compiler refuses a program. */
|
|
65
|
+
export function compileAnePrograms(options: AneInstallOptions & { readonly onProgram?: (result: AneInstallResult) => void } = {}):
|
|
66
|
+
AneInstallResult[] {
|
|
67
|
+
return aneInstallPrograms(options).map(program => {
|
|
68
|
+
const started = performance.now();
|
|
69
|
+
const status = AneProgram.compile(program.spec);
|
|
70
|
+
const result = { ...program, status, milliseconds: performance.now() - started };
|
|
71
|
+
options.onProgram?.(result);
|
|
72
|
+
return result;
|
|
73
|
+
});
|
|
74
|
+
}
|