cactus-compute 2.2.0__tar.gz → 2.2.2__tar.gz
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.
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/PKG-INFO +1 -1
- cactus_compute-2.2.2/cactus/_version.py +1 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/bindings/cactus.py +30 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_audio_buckets.py +15 -4
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_parakeet_capture.py +4 -2
- cactus_compute-2.2.2/cactus/convert/tests/test_parakeet_local_attention.py +125 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/aten_ops.py +1 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/audio_preprocess.py +6 -1
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/canonicalize/cleanup.py +1 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/importers.py +18 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/lower.py +20 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/ops.py +1 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/tdt_runtime.py +86 -42
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/input_processor.py +1 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/ModelProfiles/profiles.py +1 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus_compute.egg-info/PKG-INFO +1 -1
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus_compute.egg-info/SOURCES.txt +1 -0
- cactus_compute-2.2.0/cactus/_version.py +0 -1
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/README.md +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/__main__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/assets/test.wav +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/assets/test_monkey.png +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/bindings/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/auth.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/clean.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/code.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/common.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/compile.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/config_utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/convert.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/download.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/list.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/model.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/run.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/runtime.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/serve.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/test.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/transcribe.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/transpile.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/transpiler.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/upload.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/cli/utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/assets/gemma-4-e2b-it/probe.pt +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/cactus_adapters/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/cactus_adapters/config_utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/cactus_adapters/tensor_io.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/cactus_adapters/tokenizer.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/cactus_adapters/weight_patterns.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/calibration/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/calibration/hessian.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/calibration/loaders.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/cli.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/compat.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/export/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/export/files.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/export/qdq.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/export/reports.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/export/validate.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/handoff_probe.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/interleave_orthogonal_cq4.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/model_adapters/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/model_adapters/adapters.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/model_adapters/detection.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/model_adapters/naming.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/model_adapters/nemo.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/model_adapters/policy.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/quantization/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/quantization/cq.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_cq.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_gemma4_adapter.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_hessian.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_lfm2_adapter.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_naming_qdq.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_policy.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_qwen_adapter.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/convert/tests/test_reports.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/models/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/models/needle/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/models/needle/configuration_needle.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/models/needle/modeling_needle.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/models/needle/tokenization_needle.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/py.typed +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/server.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/canonicalize/utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/capture_jax.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/capture_pytorch.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/component_partition.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/component_pipeline.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/component_plan.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/attention.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/common.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/conv.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/deltanet.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/linear.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/lstm.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/mlp.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/rel_pos_bias.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/rms_norm.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/fusion/rope.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/graph_ir.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/hf_model.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/import_ir.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/import_semantics.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/jax_semantic_rewrites.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/jax_user_graph_bundle.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/media_limits.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/model_adapters.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/model_patterns.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/model_profiles.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/multimodal_runtime.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/normalize.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/npu/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/npu/audio.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/npu/coremltools_patches.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/npu/pipeline.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/npu/source.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/npu/vision.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/optimize_graph.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/runtime_compat.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/runtime_support.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/weight_binding.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpile/weight_compat.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/cache_utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/constants.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/convert.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/models.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/overrides.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/version.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/edges.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_builders.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_cache_conv.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_defs/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_defs/fusion_attention.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_defs/fusion_direct.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_defs/fusion_moe.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_defs/fusion_neural.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusion_defs/fusion_special.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/fusions.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/models.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Fusions/nodes.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/component_split.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/component_split_boundaries.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/component_split_builder.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/component_split_plans.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/component_split_types.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/component_splits/component_split_utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/constants.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/errors.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/generate.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_basic_ops.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_cache.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_constant_ops.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_core.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_nn_ops.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_special_ops.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/lowerings/lowering_weights.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/models.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Generator/version.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/constants.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/extra_matchers/__init__.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/extra_matchers/extra_matcher_cache.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/extra_matchers/extra_matcher_common.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/extra_matchers/extra_matcher_generic.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/extra_matchers/extra_matcher_linear.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/extra_matchers/extra_matcher_moe.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/match.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/match_utils.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/models.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/simplify_ir.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/special_fusions.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/IR/version.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/ModelProfiles/combinations.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/ModelProfiles/components.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/ModelProfiles/models.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/ModelProfiles/routes.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/RuntimePlan/models.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus_compute.egg-info/dependency_links.txt +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus_compute.egg-info/entry_points.txt +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus_compute.egg-info/requires.txt +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus_compute.egg-info/top_level.txt +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/pyproject.toml +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/setup.cfg +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_bindings.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_chat_template_golden.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_cli_run.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_cli_transpile_defaults.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_cloud_handoff_live.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_component_plan.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_download.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_generator_component_split.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_graph.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_model.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_runtime_plan.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_server.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_server_live.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_tool_constraint_state.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_transpile_aten_ops.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_transpile_importers.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_transpile_weight_compat.py +0 -0
- {cactus_compute-2.2.0 → cactus_compute-2.2.2}/tests/test_transpiler_reporting.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.2"
|
|
@@ -568,6 +568,11 @@ _lib.cactus_graph_rel_pos_bias.argtypes = [
|
|
|
568
568
|
cactus_graph_t, cactus_node_t, cactus_node_t, ctypes.c_float, ctypes.POINTER(cactus_node_t)
|
|
569
569
|
]
|
|
570
570
|
_lib.cactus_graph_rel_pos_bias.restype = ctypes.c_int
|
|
571
|
+
_lib.cactus_graph_rel_pos_attention.argtypes = [
|
|
572
|
+
cactus_graph_t, cactus_node_t, cactus_node_t, cactus_node_t, cactus_node_t, cactus_node_t,
|
|
573
|
+
ctypes.c_bool, cactus_node_t, ctypes.c_float, ctypes.c_size_t, ctypes.POINTER(cactus_node_t)
|
|
574
|
+
]
|
|
575
|
+
_lib.cactus_graph_rel_pos_attention.restype = ctypes.c_int
|
|
571
576
|
_lib.cactus_graph_attention_int8_hybrid.argtypes = [
|
|
572
577
|
cactus_graph_t, cactus_node_t, cactus_node_t, cactus_node_t, ctypes.c_float, ctypes.c_size_t,
|
|
573
578
|
ctypes.POINTER(ctypes.c_int8), ctypes.POINTER(ctypes.c_int8),
|
|
@@ -2570,6 +2575,31 @@ class Graph:
|
|
|
2570
2575
|
raise RuntimeError(_err("graph_rel_pos_bias failed"))
|
|
2571
2576
|
return self._apply_backend(self._tensor_from_node(out.value), backend)
|
|
2572
2577
|
|
|
2578
|
+
def rel_pos_attention(self, query, key, value, rel_query, relative_key, key_mask, scale, window_size=0, backend=None):
|
|
2579
|
+
query = self._ensure_tensor(query)
|
|
2580
|
+
key = self._ensure_tensor(key)
|
|
2581
|
+
value = self._ensure_tensor(value)
|
|
2582
|
+
rel_query = self._ensure_tensor(rel_query)
|
|
2583
|
+
relative_key = self._ensure_tensor(relative_key)
|
|
2584
|
+
key_mask = None if key_mask is None else self._ensure_tensor(key_mask)
|
|
2585
|
+
out = cactus_node_t()
|
|
2586
|
+
rc = _lib.cactus_graph_rel_pos_attention(
|
|
2587
|
+
self.h,
|
|
2588
|
+
cactus_node_t(query.id),
|
|
2589
|
+
cactus_node_t(key.id),
|
|
2590
|
+
cactus_node_t(value.id),
|
|
2591
|
+
cactus_node_t(rel_query.id),
|
|
2592
|
+
cactus_node_t(relative_key.id),
|
|
2593
|
+
ctypes.c_bool(key_mask is not None),
|
|
2594
|
+
cactus_node_t(0 if key_mask is None else key_mask.id),
|
|
2595
|
+
ctypes.c_float(float(scale)),
|
|
2596
|
+
ctypes.c_size_t(int(window_size)),
|
|
2597
|
+
ctypes.byref(out),
|
|
2598
|
+
)
|
|
2599
|
+
if rc != 0:
|
|
2600
|
+
raise RuntimeError(_err("graph_rel_pos_attention failed"))
|
|
2601
|
+
return self._apply_backend(self._tensor_from_node(out.value), backend)
|
|
2602
|
+
|
|
2573
2603
|
def attention_int8_hybrid(self, query, key_new, value_new, scale, position_offset,
|
|
2574
2604
|
cached_keys, cached_values, k_scales, v_scales,
|
|
2575
2605
|
cache_len, num_kv_heads, head_dim, window_size=0, backend=None):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from cactus.transpile.audio_preprocess import audio_bucket_frames
|
|
2
2
|
from cactus.transpile.audio_preprocess import audio_bucket_seconds
|
|
3
|
+
from cactus.transpile.audio_preprocess import audio_capture_frames
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
def test_fibonacci_ladder_capped_at_model_window():
|
|
@@ -26,8 +27,18 @@ def test_buckets_are_sorted_and_unique():
|
|
|
26
27
|
assert len(buckets) == len(set(buckets))
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
def
|
|
30
|
-
assert audio_bucket_seconds() == (1, 2, 3, 5, 8, 13, 21)
|
|
30
|
+
def test_default_ladder_seconds():
|
|
31
|
+
assert audio_bucket_seconds() == (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 300, 377)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_capture_window_is_420_seconds():
|
|
35
|
+
assert audio_capture_frames() == 42000
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_env_override_keeps_capture_window(monkeypatch):
|
|
39
|
+
monkeypatch.setenv("CACTUS_TRANSPILER_AUDIO_BUCKETS", "30,60")
|
|
40
|
+
assert audio_capture_frames() == 42000
|
|
41
|
+
assert audio_bucket_frames(audio_capture_frames()) == [3000, 6000, 42000]
|
|
31
42
|
|
|
32
43
|
|
|
33
44
|
def test_env_override_replaces_ladder(monkeypatch):
|
|
@@ -43,9 +54,9 @@ def test_env_override_is_sorted_and_deduplicated(monkeypatch):
|
|
|
43
54
|
|
|
44
55
|
def test_malformed_env_override_falls_back_to_default(monkeypatch):
|
|
45
56
|
monkeypatch.setenv("CACTUS_TRANSPILER_AUDIO_BUCKETS", "not-a-number")
|
|
46
|
-
assert audio_bucket_seconds() == (1, 2, 3, 5, 8, 13, 21)
|
|
57
|
+
assert audio_bucket_seconds() == (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 300, 377)
|
|
47
58
|
|
|
48
59
|
|
|
49
60
|
def test_empty_env_override_falls_back_to_default(monkeypatch):
|
|
50
61
|
monkeypatch.setenv("CACTUS_TRANSPILER_AUDIO_BUCKETS", "0,-3")
|
|
51
|
-
assert audio_bucket_seconds() == (1, 2, 3, 5, 8, 13, 21)
|
|
62
|
+
assert audio_bucket_seconds() == (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 300, 377)
|
|
@@ -19,9 +19,11 @@ def test_capture_window_is_independent_of_validation_audio():
|
|
|
19
19
|
with patch.dict("os.environ", {"CACTUS_TRANSPILER_AUDIO_BUCKETS": ""}):
|
|
20
20
|
specs = build_parakeet_tdt_component_specs(model, named_tensors={"input_features": features})
|
|
21
21
|
encoders = specs[:-1]
|
|
22
|
-
assert [int(spec.metadata["audio_frames"]) for spec in encoders] == [
|
|
22
|
+
assert [int(spec.metadata["audio_frames"]) for spec in encoders] == [
|
|
23
|
+
100, 200, 300, 500, 800, 1300, 2100, 3400, 5500, 8900, 14400, 23300, 30000, 37700, 42000,
|
|
24
|
+
]
|
|
23
25
|
assert encoders[-1].component == "audio_encoder"
|
|
24
|
-
assert tuple(encoders[-1].example_inputs[0].shape) == (1,
|
|
26
|
+
assert tuple(encoders[-1].example_inputs[0].shape) == (1, 42000, 128)
|
|
25
27
|
assert torch.all(features == 2.0)
|
|
26
28
|
assert not torch.all(encoders[-1].example_inputs[0] == 2.0)
|
|
27
29
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from types import SimpleNamespace
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import torch
|
|
5
|
+
|
|
6
|
+
from cactus.transpile.tdt_runtime import ParakeetTDTSelfAttention
|
|
7
|
+
from cactus.transpile.tdt_runtime import rel_pos_attention
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _random_inputs(seq_len, rel_len, *, heads=2, dim=16, padded=3, seed=0):
|
|
11
|
+
generator = torch.Generator().manual_seed(seed)
|
|
12
|
+
shape = (1, seq_len, heads, dim)
|
|
13
|
+
tensors = [torch.randn(shape, generator=generator) for _ in range(4)]
|
|
14
|
+
relative_key = torch.randn((1, rel_len, heads, dim), generator=generator)
|
|
15
|
+
key_mask = torch.zeros((1, seq_len))
|
|
16
|
+
key_mask[:, seq_len - padded:] = -10000.0
|
|
17
|
+
return (*tensors, relative_key, key_mask)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _dense_reference(query, key, value, rel_query, relative_key, key_mask, scale, window):
|
|
21
|
+
seq_len = query.shape[1]
|
|
22
|
+
center = (relative_key.shape[1] - 1) // 2
|
|
23
|
+
out = torch.empty_like(query)
|
|
24
|
+
for t in range(seq_len):
|
|
25
|
+
for h in range(query.shape[2]):
|
|
26
|
+
scores = []
|
|
27
|
+
for j in range(seq_len):
|
|
28
|
+
if window > 0 and abs(t - j) > window:
|
|
29
|
+
scores.append(float("-inf"))
|
|
30
|
+
continue
|
|
31
|
+
content = torch.dot(query[0, t, h], key[0, j, h])
|
|
32
|
+
position = torch.dot(rel_query[0, t, h], relative_key[0, center - (t - j), h])
|
|
33
|
+
scores.append(float((content + position) * scale + key_mask[0, j]))
|
|
34
|
+
weights = torch.softmax(torch.tensor(scores), dim=0)
|
|
35
|
+
out[0, t, h] = weights @ value[0, :, h]
|
|
36
|
+
return out
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_rel_pos_attention_matches_dense_reference():
|
|
40
|
+
for seq_len, window, rel_len in ((40, 6, 13), (40, 0, 79), (300, 20, 41)):
|
|
41
|
+
inputs = _random_inputs(seq_len, rel_len)
|
|
42
|
+
expected = _dense_reference(*inputs, 0.25, window)
|
|
43
|
+
actual = rel_pos_attention(*inputs, 0.25, window)
|
|
44
|
+
torch.testing.assert_close(actual, expected, atol=1e-5, rtol=1e-5)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _attention_layer(hidden=32, heads=4):
|
|
48
|
+
torch.manual_seed(0)
|
|
49
|
+
state_dict = {f"layer.{name}.weight": torch.randn(hidden, hidden) * 0.2
|
|
50
|
+
for name in ("linear_q", "linear_k", "linear_v", "linear_out", "linear_pos")}
|
|
51
|
+
state_dict["layer.pos_bias_u"] = torch.randn(heads, hidden // heads)
|
|
52
|
+
state_dict["layer.pos_bias_v"] = torch.randn(heads, hidden // heads)
|
|
53
|
+
config = SimpleNamespace(
|
|
54
|
+
hidden_dim=hidden,
|
|
55
|
+
attention_heads=heads,
|
|
56
|
+
attention_head_dim=hidden // heads,
|
|
57
|
+
attention_scale=(hidden // heads) ** -0.5,
|
|
58
|
+
)
|
|
59
|
+
return ParakeetTDTSelfAttention(config, "layer", state_dict).eval()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_local_attention_exports_single_semantic_op():
|
|
63
|
+
from cactus.transpile.capture_pytorch import capture_model
|
|
64
|
+
|
|
65
|
+
class _Windowed(torch.nn.Module):
|
|
66
|
+
def __init__(self):
|
|
67
|
+
super().__init__()
|
|
68
|
+
self.attention = _attention_layer()
|
|
69
|
+
|
|
70
|
+
def forward(self, x, mask):
|
|
71
|
+
return self.attention(x, mask, 4)
|
|
72
|
+
|
|
73
|
+
captured = capture_model(_Windowed().eval(), (torch.randn(1, 12, 32), torch.zeros(1, 1, 1, 12)), strict=False)
|
|
74
|
+
nodes = [
|
|
75
|
+
captured.ir_graph.nodes[node_id]
|
|
76
|
+
for node_id in captured.ir_graph.order
|
|
77
|
+
if captured.ir_graph.nodes[node_id].op == "rel_pos_attention"
|
|
78
|
+
]
|
|
79
|
+
assert len(nodes) == 1
|
|
80
|
+
assert nodes[0].attrs == {"scale": 8 ** -0.5, "window_size": 4}
|
|
81
|
+
assert len(nodes[0].inputs) == 6
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_engine_op_matches_torch_reference():
|
|
85
|
+
from cactus.bindings.cactus import Graph
|
|
86
|
+
|
|
87
|
+
for seq_len, window, rel_len in ((150, 20, 41), (70, 0, 139)):
|
|
88
|
+
inputs = _random_inputs(seq_len, rel_len, dim=32, padded=5, seed=1)
|
|
89
|
+
inputs = tuple(tensor.half().float() for tensor in inputs)
|
|
90
|
+
expected = rel_pos_attention(*inputs, 32 ** -0.5, window)
|
|
91
|
+
|
|
92
|
+
graph = Graph()
|
|
93
|
+
nodes = [graph.input(tuple(tensor.shape)) for tensor in inputs]
|
|
94
|
+
out = graph.rel_pos_attention(*nodes, 32 ** -0.5, window)
|
|
95
|
+
for node, tensor in zip(nodes, inputs):
|
|
96
|
+
graph.set_input(node, tensor.numpy().astype(np.float16))
|
|
97
|
+
graph.execute()
|
|
98
|
+
np.testing.assert_allclose(out.numpy().astype(np.float32), expected.numpy(), atol=3e-3)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_chunked_subsampling_matches_single_pass():
|
|
102
|
+
from cactus.transpile.tdt_runtime import ParakeetTDTPreEncode
|
|
103
|
+
|
|
104
|
+
torch.manual_seed(0)
|
|
105
|
+
channels, mels, hidden = 4, 16, 8
|
|
106
|
+
state_dict = {
|
|
107
|
+
"encoder.pre_encode.conv.0.weight": torch.randn(channels, 1, 3, 3),
|
|
108
|
+
"encoder.pre_encode.conv.0.bias": torch.randn(channels),
|
|
109
|
+
"encoder.pre_encode.out.weight": torch.randn(hidden, channels * 2),
|
|
110
|
+
"encoder.pre_encode.out.bias": torch.randn(hidden),
|
|
111
|
+
}
|
|
112
|
+
for index in (2, 5):
|
|
113
|
+
state_dict[f"encoder.pre_encode.conv.{index}.weight"] = torch.randn(channels, 1, 3, 3)
|
|
114
|
+
state_dict[f"encoder.pre_encode.conv.{index}.bias"] = torch.randn(channels)
|
|
115
|
+
state_dict[f"encoder.pre_encode.conv.{index + 1}.weight"] = torch.randn(channels, channels, 1, 1)
|
|
116
|
+
state_dict[f"encoder.pre_encode.conv.{index + 1}.bias"] = torch.randn(channels)
|
|
117
|
+
state_dict = {name: tensor * 0.3 for name, tensor in state_dict.items()}
|
|
118
|
+
config = SimpleNamespace(subsampling_conv_channels=channels, num_mel_bins=mels, hidden_dim=hidden)
|
|
119
|
+
pre_encode = ParakeetTDTPreEncode(config, state_dict).eval()
|
|
120
|
+
features = torch.randn(1, 7003, mels)
|
|
121
|
+
with torch.no_grad():
|
|
122
|
+
chunked = pre_encode(features)
|
|
123
|
+
single = pre_encode._subsample(features)
|
|
124
|
+
assert chunked.shape == single.shape
|
|
125
|
+
torch.testing.assert_close(chunked, single, atol=1e-5, rtol=1e-5)
|
|
@@ -54,6 +54,7 @@ _EXACT_RULES: dict[str, str] = {
|
|
|
54
54
|
"qwen2_moe_layer_gated.default": "qwen2_moe_layer_gated",
|
|
55
55
|
"cactus_transpile.gemma4_moe_layer_gated.default": "gemma4_moe_layer_gated",
|
|
56
56
|
"gemma4_moe_layer_gated.default": "gemma4_moe_layer_gated",
|
|
57
|
+
"rel_pos_attention.default": "rel_pos_attention",
|
|
57
58
|
# Keep the legacy importer key until the importer itself is renamed.
|
|
58
59
|
"aten.diff.default": "aten.diff.default",
|
|
59
60
|
}
|
|
@@ -21,7 +21,8 @@ _PARAKEET_HOP_LENGTH = 160
|
|
|
21
21
|
_PARAKEET_PREEMPHASIS = 0.97
|
|
22
22
|
_PARAKEET_LOG_FLOOR = np.float32(2**-24)
|
|
23
23
|
_DEFAULT_MAX_AUDIO_SECONDS = 30.0
|
|
24
|
-
_DEFAULT_AUDIO_BUCKET_SECONDS = (1, 2, 3, 5, 8, 13, 21)
|
|
24
|
+
_DEFAULT_AUDIO_BUCKET_SECONDS = (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 300, 377)
|
|
25
|
+
_AUDIO_CAPTURE_SECONDS = 420
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def audio_duration_limit_seconds() -> float:
|
|
@@ -47,6 +48,10 @@ def audio_bucket_seconds() -> tuple[int, ...]:
|
|
|
47
48
|
return positive
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
def audio_capture_frames() -> int:
|
|
52
|
+
return _AUDIO_CAPTURE_SECONDS * (_PARAKEET_SAMPLE_RATE // _PARAKEET_HOP_LENGTH)
|
|
53
|
+
|
|
54
|
+
|
|
50
55
|
def audio_bucket_frames(max_frames: int) -> list[int]:
|
|
51
56
|
if max_frames <= 0:
|
|
52
57
|
return []
|
|
@@ -1860,6 +1860,23 @@ def import_gemma4_moe_layer_gated(ir, node, ctx, *, shape, dtype, torch_op):
|
|
|
1860
1860
|
op="gemma4_moe_layer_gated", has_expert_bias=False, activation="gelu")
|
|
1861
1861
|
|
|
1862
1862
|
|
|
1863
|
+
def import_rel_pos_attention(ir, node, ctx, *, shape, dtype, torch_op):
|
|
1864
|
+
if len(node.args) != 8:
|
|
1865
|
+
ctx.fail(f"rel_pos_attention expected 8 args, got {len(node.args)}")
|
|
1866
|
+
ir_node = IRNode(
|
|
1867
|
+
id=node_id(node),
|
|
1868
|
+
op="rel_pos_attention",
|
|
1869
|
+
inputs=[value_id(node.args[i], ctx) for i in range(6)],
|
|
1870
|
+
outputs=[value_id(node, ctx)],
|
|
1871
|
+
attrs={
|
|
1872
|
+
"scale": float(extract_literals(node.args[6])),
|
|
1873
|
+
"window_size": int(extract_literals(node.args[7])),
|
|
1874
|
+
},
|
|
1875
|
+
meta=_base_meta(shape, dtype, torch_op, node),
|
|
1876
|
+
)
|
|
1877
|
+
register_node(ir, ir_node, shape=shape, dtype=dtype)
|
|
1878
|
+
|
|
1879
|
+
|
|
1863
1880
|
OP_IMPORTERS = {
|
|
1864
1881
|
"arange": import_arange,
|
|
1865
1882
|
"identity": import_identity,
|
|
@@ -1957,4 +1974,5 @@ OP_IMPORTERS = {
|
|
|
1957
1974
|
"lfm2_moe_layer_gated": import_lfm2_moe_layer_gated,
|
|
1958
1975
|
"qwen2_moe_layer_gated": import_qwen2_moe_layer_gated,
|
|
1959
1976
|
"gemma4_moe_layer_gated": import_gemma4_moe_layer_gated,
|
|
1977
|
+
"rel_pos_attention": import_rel_pos_attention,
|
|
1960
1978
|
}
|
|
@@ -2218,6 +2218,26 @@ def _lower_ir_node(g: Graph, node: IRNode, env: dict[str, Any], ir: IRGraph) ->
|
|
|
2218
2218
|
)
|
|
2219
2219
|
return [g.rel_pos_bias(query, relative_key, float(node.attrs.get("scale", 1.0)))]
|
|
2220
2220
|
|
|
2221
|
+
if op == "rel_pos_attention":
|
|
2222
|
+
query, key, value, rel_query, relative_key, key_mask = (
|
|
2223
|
+
_ensure_fp16_tensor(g, _tensor(env, input_id)) for input_id in node.inputs
|
|
2224
|
+
)
|
|
2225
|
+
mask_shape = (int(query.shape[0]), int(query.shape[1]))
|
|
2226
|
+
if tuple(int(dim) for dim in key_mask.shape) != mask_shape:
|
|
2227
|
+
key_mask = g.reshape(key_mask, mask_shape)
|
|
2228
|
+
return [
|
|
2229
|
+
g.rel_pos_attention(
|
|
2230
|
+
query,
|
|
2231
|
+
key,
|
|
2232
|
+
value,
|
|
2233
|
+
rel_query,
|
|
2234
|
+
relative_key,
|
|
2235
|
+
key_mask,
|
|
2236
|
+
float(node.attrs["scale"]),
|
|
2237
|
+
int(node.attrs.get("window_size", 0)),
|
|
2238
|
+
)
|
|
2239
|
+
]
|
|
2240
|
+
|
|
2221
2241
|
if op in {"gated_deltanet_prefill", "gated_deltanet_decode"}:
|
|
2222
2242
|
x = _tensor(env, node.inputs[0])
|
|
2223
2243
|
qkv_weight = _tensor(env, node.inputs[1])
|
|
@@ -147,6 +147,7 @@ OPS: tuple[OpSchema, ...] = (
|
|
|
147
147
|
OpSchema("rms_norm", num_inputs=2, attrs=("eps",), backend_op="rms_norm"),
|
|
148
148
|
OpSchema("rope", num_inputs=1, attrs=("theta", "position_offset"), backend_op="rope"),
|
|
149
149
|
OpSchema("rel_pos_bias", num_inputs=2, attrs=("scale",), backend_op="rel_pos_bias"),
|
|
150
|
+
OpSchema("rel_pos_attention", num_inputs=6, attrs=("scale", "window_size"), backend_op="rel_pos_attention"),
|
|
150
151
|
OpSchema(
|
|
151
152
|
"attention",
|
|
152
153
|
num_inputs=3,
|
|
@@ -14,11 +14,63 @@ import torch.nn.functional as F
|
|
|
14
14
|
|
|
15
15
|
from cactus.transpile.component_pipeline import ComponentModuleSpec
|
|
16
16
|
from cactus.transpile.audio_preprocess import audio_bucket_frames
|
|
17
|
+
from cactus.transpile.audio_preprocess import audio_capture_frames
|
|
17
18
|
from cactus.transpile.audio_preprocess import prepare_native_parakeet_audio_features
|
|
18
19
|
from cactus.transpile.model_profiles import add_tensor_aliases
|
|
19
20
|
from cactus.transpile.model_profiles import PARAKEET_TDT_PROFILE
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
_FULL_ATTENTION_MAX_ENCODER_FRAMES = 688
|
|
24
|
+
_LOCAL_ATTENTION_WINDOW = 256
|
|
25
|
+
_REL_POS_ATTENTION_QUERY_BLOCK = 256
|
|
26
|
+
_PRE_ENCODE_CHUNK_FRAMES = 3000
|
|
27
|
+
_PRE_ENCODE_HALO_FRAMES = 16
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@torch.library.custom_op("cactus_transpile::rel_pos_attention", mutates_args=())
|
|
31
|
+
def rel_pos_attention(
|
|
32
|
+
query: torch.Tensor,
|
|
33
|
+
key: torch.Tensor,
|
|
34
|
+
value: torch.Tensor,
|
|
35
|
+
rel_query: torch.Tensor,
|
|
36
|
+
relative_key: torch.Tensor,
|
|
37
|
+
key_mask: torch.Tensor,
|
|
38
|
+
scale: float,
|
|
39
|
+
window: int,
|
|
40
|
+
) -> torch.Tensor:
|
|
41
|
+
seq_len = int(query.shape[1])
|
|
42
|
+
rel_len = int(relative_key.shape[1])
|
|
43
|
+
center = (rel_len - 1) // 2
|
|
44
|
+
reach = seq_len - 1 if window <= 0 or window >= seq_len else window
|
|
45
|
+
q = query.permute(0, 2, 1, 3).float()
|
|
46
|
+
k = key.permute(0, 2, 1, 3).float()
|
|
47
|
+
v = value.permute(0, 2, 1, 3).float()
|
|
48
|
+
q_rel = rel_query.permute(0, 2, 1, 3).float()
|
|
49
|
+
r = relative_key.permute(0, 2, 1, 3).float()
|
|
50
|
+
mask = key_mask.float()
|
|
51
|
+
out = torch.empty_like(q)
|
|
52
|
+
for t0 in range(0, seq_len, _REL_POS_ATTENTION_QUERY_BLOCK):
|
|
53
|
+
t1 = min(seq_len, t0 + _REL_POS_ATTENTION_QUERY_BLOCK)
|
|
54
|
+
k0 = max(0, t0 - reach)
|
|
55
|
+
k1 = min(seq_len, t1 + reach)
|
|
56
|
+
offset = (
|
|
57
|
+
torch.arange(k0, k1, device=query.device).view(1, -1)
|
|
58
|
+
- torch.arange(t0, t1, device=query.device).view(-1, 1)
|
|
59
|
+
)
|
|
60
|
+
rel_index = (center + offset).clamp(0, rel_len - 1).expand(q.shape[0], q.shape[1], -1, -1)
|
|
61
|
+
content = q[:, :, t0:t1] @ k[:, :, k0:k1].transpose(-1, -2)
|
|
62
|
+
position = (q_rel[:, :, t0:t1] @ r.transpose(-1, -2)).gather(-1, rel_index)
|
|
63
|
+
scores = (content + position) * scale + mask[:, None, None, k0:k1]
|
|
64
|
+
scores = scores.masked_fill(offset.abs() > reach, float("-inf"))
|
|
65
|
+
out[:, :, t0:t1] = scores.softmax(dim=-1) @ v[:, :, k0:k1]
|
|
66
|
+
return out.permute(0, 2, 1, 3).to(query.dtype).contiguous()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@rel_pos_attention.register_fake
|
|
70
|
+
def _rel_pos_attention_fake(query, key, value, rel_query, relative_key, key_mask, scale, window):
|
|
71
|
+
return torch.empty_like(query)
|
|
72
|
+
|
|
73
|
+
|
|
22
74
|
def _cfg_get(config: dict[str, Any], key: str, default: Any = None) -> Any:
|
|
23
75
|
value = config.get(key, default)
|
|
24
76
|
return default if value is None else value
|
|
@@ -403,19 +455,6 @@ def _relative_position_embeddings(*, seq_len: int, hidden_dim: int, device: torc
|
|
|
403
455
|
return embeddings.to(dtype=dtype)
|
|
404
456
|
|
|
405
457
|
|
|
406
|
-
def _relative_position_bias(query: torch.Tensor, relative_key: torch.Tensor, *, scale: float) -> torch.Tensor:
|
|
407
|
-
batch, heads, seq_len, _ = query.shape
|
|
408
|
-
scores = torch.matmul(query, relative_key.transpose(-1, -2))
|
|
409
|
-
rel_index = (
|
|
410
|
-
torch.arange(seq_len, device=query.device).view(seq_len, 1)
|
|
411
|
-
- torch.arange(seq_len, device=query.device).view(1, seq_len)
|
|
412
|
-
+ (seq_len - 1)
|
|
413
|
-
)
|
|
414
|
-
rel_index = rel_index.view(1, 1, seq_len, seq_len).expand(batch, heads, seq_len, seq_len)
|
|
415
|
-
gathered = scores.gather(-1, rel_index)
|
|
416
|
-
return gathered * float(scale)
|
|
417
|
-
|
|
418
|
-
|
|
419
458
|
class ParakeetTDTFeedForward(nn.Module):
|
|
420
459
|
def __init__(self, config: ParakeetTDTConfig, prefix: str, state_dict: dict[str, torch.Tensor]):
|
|
421
460
|
super().__init__()
|
|
@@ -457,7 +496,7 @@ class ParakeetTDTSelfAttention(nn.Module):
|
|
|
457
496
|
self.pos_bias_v = nn.Parameter(state_dict[f"{prefix}.pos_bias_v"].clone())
|
|
458
497
|
self.config = config
|
|
459
498
|
|
|
460
|
-
def forward(self, x: torch.Tensor, attn_mask: torch.Tensor) -> torch.Tensor:
|
|
499
|
+
def forward(self, x: torch.Tensor, attn_mask: torch.Tensor, window: int = 0) -> torch.Tensor:
|
|
461
500
|
batch, seq_len, _ = x.shape
|
|
462
501
|
num_heads = self.config.attention_heads
|
|
463
502
|
head_dim = self.config.attention_head_dim
|
|
@@ -466,39 +505,29 @@ class ParakeetTDTSelfAttention(nn.Module):
|
|
|
466
505
|
k = self.linear_k(x).view(batch, seq_len, num_heads, head_dim)
|
|
467
506
|
v = self.linear_v(x).view(batch, seq_len, num_heads, head_dim)
|
|
468
507
|
|
|
508
|
+
reach = window if window > 0 else seq_len - 1
|
|
469
509
|
rel_pos = _relative_position_embeddings(
|
|
470
|
-
seq_len=
|
|
510
|
+
seq_len=reach + 1,
|
|
471
511
|
hidden_dim=self.config.hidden_dim,
|
|
472
512
|
device=x.device,
|
|
473
513
|
dtype=x.dtype,
|
|
474
514
|
)
|
|
475
|
-
rel_k = self.linear_pos(rel_pos).view(1, 2 *
|
|
515
|
+
rel_k = self.linear_pos(rel_pos).view(1, 2 * reach + 1, num_heads, head_dim)
|
|
476
516
|
|
|
477
517
|
q_u = q + self.pos_bias_u.view(1, 1, num_heads, head_dim).to(dtype=x.dtype, device=x.device)
|
|
478
518
|
q_v = q + self.pos_bias_v.view(1, 1, num_heads, head_dim).to(dtype=x.dtype, device=x.device)
|
|
479
519
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
scale=self.config.attention_scale,
|
|
490
|
-
)
|
|
491
|
-
rel_bias = rel_bias + attn_mask
|
|
492
|
-
attn = F.scaled_dot_product_attention(
|
|
493
|
-
q_u_heads,
|
|
494
|
-
k_heads,
|
|
495
|
-
v_heads,
|
|
496
|
-
attn_mask=rel_bias,
|
|
497
|
-
dropout_p=0.0,
|
|
498
|
-
is_causal=False,
|
|
520
|
+
attn = rel_pos_attention(
|
|
521
|
+
q_u,
|
|
522
|
+
k,
|
|
523
|
+
v,
|
|
524
|
+
q_v,
|
|
525
|
+
rel_k,
|
|
526
|
+
attn_mask.reshape(batch, seq_len),
|
|
527
|
+
self.config.attention_scale,
|
|
528
|
+
window,
|
|
499
529
|
)
|
|
500
|
-
|
|
501
|
-
return self.linear_out(attn)
|
|
530
|
+
return self.linear_out(attn.reshape(batch, seq_len, self.config.hidden_dim))
|
|
502
531
|
|
|
503
532
|
|
|
504
533
|
class ParakeetTDTConformerConv(nn.Module):
|
|
@@ -583,9 +612,9 @@ class ParakeetTDTEncoderLayer(nn.Module):
|
|
|
583
612
|
module.bias.data.copy_(state_dict[f"{prefix}.{name}.bias"].to(dtype=module.bias.dtype))
|
|
584
613
|
self.config = config
|
|
585
614
|
|
|
586
|
-
def forward(self, x: torch.Tensor, conv_mask: torch.Tensor, attn_mask: torch.Tensor) -> torch.Tensor:
|
|
615
|
+
def forward(self, x: torch.Tensor, conv_mask: torch.Tensor, attn_mask: torch.Tensor, window: int = 0) -> torch.Tensor:
|
|
587
616
|
x = x + 0.5 * self.feed_forward1(self.norm_feed_forward1(x), activation=self.config.encoder_hidden_act)
|
|
588
|
-
x = x + self.self_attn(self.norm_self_att(x), attn_mask)
|
|
617
|
+
x = x + self.self_attn(self.norm_self_att(x), attn_mask, window)
|
|
589
618
|
x = x + self.conv(self.norm_conv(x), conv_mask)
|
|
590
619
|
x = x + 0.5 * self.feed_forward2(self.norm_feed_forward2(x), activation=self.config.encoder_hidden_act)
|
|
591
620
|
return self.norm_out(x)
|
|
@@ -624,6 +653,19 @@ class ParakeetTDTPreEncode(nn.Module):
|
|
|
624
653
|
def forward(self, input_features: torch.Tensor) -> torch.Tensor:
|
|
625
654
|
if input_features.ndim != 3:
|
|
626
655
|
raise ValueError(f"expected input_features [batch, frames, mels], got {tuple(input_features.shape)}")
|
|
656
|
+
frames = int(input_features.shape[1])
|
|
657
|
+
if frames <= _PRE_ENCODE_CHUNK_FRAMES:
|
|
658
|
+
return self._subsample(input_features)
|
|
659
|
+
chunks = []
|
|
660
|
+
for start in range(0, frames, _PRE_ENCODE_CHUNK_FRAMES):
|
|
661
|
+
end = min(frames, start + _PRE_ENCODE_CHUNK_FRAMES)
|
|
662
|
+
low = max(0, start - _PRE_ENCODE_HALO_FRAMES)
|
|
663
|
+
out = self._subsample(input_features[:, low : min(frames, end + _PRE_ENCODE_HALO_FRAMES)])
|
|
664
|
+
first = (start - low) // 8
|
|
665
|
+
chunks.append(out[:, first : first + (end - start + 7) // 8])
|
|
666
|
+
return torch.cat(chunks, dim=1)
|
|
667
|
+
|
|
668
|
+
def _subsample(self, input_features: torch.Tensor) -> torch.Tensor:
|
|
627
669
|
x = input_features.unsqueeze(1)
|
|
628
670
|
x = F.relu(self.conv[0](x))
|
|
629
671
|
x = F.relu(self.conv[3](self.conv[2](x)))
|
|
@@ -646,8 +688,9 @@ class ParakeetTDTEncoder(nn.Module):
|
|
|
646
688
|
pad_mask = input_features_mask[:, :: self.config.subsampling_factor]
|
|
647
689
|
conv_mask = pad_mask.unsqueeze(1)
|
|
648
690
|
attn_mask = ((pad_mask - 1.0) * 10000.0).unsqueeze(1).unsqueeze(1)
|
|
691
|
+
window = 0 if int(x.shape[1]) <= _FULL_ATTENTION_MAX_ENCODER_FRAMES else _LOCAL_ATTENTION_WINDOW
|
|
649
692
|
for layer in self.layers:
|
|
650
|
-
x = layer(x, conv_mask, attn_mask)
|
|
693
|
+
x = layer(x, conv_mask, attn_mask, window)
|
|
651
694
|
return x
|
|
652
695
|
|
|
653
696
|
|
|
@@ -850,11 +893,12 @@ def build_parakeet_tdt_component_specs(
|
|
|
850
893
|
) -> list[ComponentModuleSpec]:
|
|
851
894
|
features = named_tensors["input_features"]
|
|
852
895
|
input_features = torch.randn(
|
|
853
|
-
(int(features.shape[0]),
|
|
896
|
+
(int(features.shape[0]), audio_capture_frames(), int(features.shape[2])),
|
|
854
897
|
device=features.device,
|
|
855
898
|
dtype=features.dtype,
|
|
856
899
|
)
|
|
857
|
-
|
|
900
|
+
example_features = input_features[:, : audio_bucket_frames(int(input_features.shape[1]))[0], :]
|
|
901
|
+
example_hidden = model.encoder(example_features, torch.ones_like(example_features[:, :, 0]))
|
|
858
902
|
batch_size = int(example_hidden.shape[0])
|
|
859
903
|
initial_states = model.initial_decoder_state(
|
|
860
904
|
batch_size=batch_size,
|
{cactus_compute-2.2.0 → cactus_compute-2.2.2}/cactus/transpiler/Converter/input_processor.py
RENAMED
|
@@ -95,6 +95,7 @@ PROCESSOR_MAP = {
|
|
|
95
95
|
"google/gemma-4-E2B": gemma4_processor,
|
|
96
96
|
"openai/whisper-tiny": default_processor,
|
|
97
97
|
"nvidia/parakeet-tdt-0.6b-v3": parakeet_processor,
|
|
98
|
+
"nvidia/parakeet-tdt-0.6b-v2": parakeet_processor,
|
|
98
99
|
"LiquidAI/LFM2-VL-3B": default_processor,
|
|
99
100
|
"Qwen/Qwen2.5-0.5B": default_processor,
|
|
100
101
|
"LiquidAI/LFM2.5-8B-A1B": text_tokenizer_processor,
|
|
@@ -586,6 +586,7 @@ MODEL_ID_MAP = {
|
|
|
586
586
|
"openai/whisper-tiny": WHISPER_PROFILE,
|
|
587
587
|
"openai/whisper-small": WHISPER_PROFILE,
|
|
588
588
|
"nvidia/parakeet-tdt-0.6b-v3": PARAKEET_PROFILE,
|
|
589
|
+
"nvidia/parakeet-tdt-0.6b-v2": PARAKEET_PROFILE,
|
|
589
590
|
"LiquidAI/LFM2-VL-450M": LFM_VLM_PROFILE,
|
|
590
591
|
"LiquidAI/LFM2-VL-3B": LFM_VLM_PROFILE,
|
|
591
592
|
"Qwen/Qwen2.5-0.5B": QWEN2_5_0_5B_PROFILE,
|
|
@@ -63,6 +63,7 @@ cactus/convert/tests/test_hessian.py
|
|
|
63
63
|
cactus/convert/tests/test_lfm2_adapter.py
|
|
64
64
|
cactus/convert/tests/test_naming_qdq.py
|
|
65
65
|
cactus/convert/tests/test_parakeet_capture.py
|
|
66
|
+
cactus/convert/tests/test_parakeet_local_attention.py
|
|
66
67
|
cactus/convert/tests/test_policy.py
|
|
67
68
|
cactus/convert/tests/test_qwen_adapter.py
|
|
68
69
|
cactus/convert/tests/test_reports.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|