Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers - #21751

Merged
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4
Aug 17, 2026
Merged

Qualcomm AI Engine Direct - [GenAI Pipeline] PR4: Adapter interfaces, default implementations & dataset providers#21751
psiddh merged 1 commit into
pytorch:mainfrom
CodeLinaro:pr4

Conversation

@qti-horodnic

Copy link
Copy Markdown
Contributor

Summary

This PR adds the adapter layer that wraps external APIs (ExecuTorch, QNN SDK, HuggingFace) behind injectable Protocol interfaces for testability. Each strategy implementation (in subsequent PRs) delegates to these adapters rather than calling external APIs directly, enabling unit tests with mocked dependencies.

What's included

Adapter Protocols (6 files):

  • QuantizerAdapter: Protocol wrapping make_quantizer, prepare_pt2e, calibrate, convert_pt2e
  • CompilerAdapter: Protocol wrapping ExportSession compilation flow + CompilationResult dataclass
  • DeviceRunnerAdapter: Protocol wrapping SimpleADB push/execute/pull + InferenceResult dataclass
  • ModelLoaderAdapter: Protocol wrapping HuggingFace model/tokenizer loading
  • CalibrationDataAdapter: Protocol for calibration dataset construction
  • TrainingDataAdapter: Protocol for QAT training data (yields (features, labels) pairs)

Default Implementations (6 files):

  • DefaultQuantizerAdapter: Delegates to export_utils.make_quantizer + torchao.quantization.pt2e
  • DefaultCompilerAdapter: Placeholder for recipe-based compilation (depends on ExportRecipe/ExportSession APIs not yet available). Raises NotImplementedError with guidance to inject a custom CompilerAdapter using to_edge_transform_and_lower_to_qnn.
  • DefaultDeviceRunnerAdapter: Delegates to SimpleADB for on-device execution
  • DefaultModelLoaderAdapter: Delegates to HuggingFace AutoModelForCausalLM + AutoTokenizer
  • DefaultCalibrationDataAdapter: Random token sequences; accepts a caller-supplied dataset or DataLoader via extra_options["dataset"]
  • DefaultTrainingDataAdapter: Pass-through for caller-supplied QAT data; raises ValueError if absent (labelled data can't be synthesized)

Configuration:

  • .coveragerc updated to omit default_*_adapter.py files (integration-test-only, require real SDK/hardware)
  • __init__.py files updated to export new adapter types

New datasets/ package

Dataset providers are a cross-stage concern, not a model-preparation detail — the same corpus feeds PTQ calibration during quantization and
on-device result evaluation during inference (including pre-built .pte flows where model preparation never runs).
They therefore live in a top-level datasets/ package rather than under strategies/model_preparation/.

PR Review Checklist

  • All new classes follow single responsibility (one class per file) - Yes.
  • All dependencies are injected via constructor with sensible defaults - Yes.
  • All external calls are behind injectable interfaces - Yes (Protocol pattern).
  • Unit tests cover every public method - Yes.
  • No existing files are modified (Phase 1 constraint) - Yes (only existing files modified are those added in previous GenAI prs).
  • Docstrings on all public classes and methods - Yes.
  • Type annotations on all function signatures - Yes.
  • Logging follows the strategy in the LLD - Yes (lazy imports, debug-level logging in defaults).

Related PRs

Test plan

python -m pytest \
backends/qualcomm/genai_pipeline/tests/ \
-v

All existing tests continue to pass (no regressions).

Test Coverage

Command to run:

python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing

Result:

Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 7 1 0 0 86% 40
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 475 10 44 1 98% 417 10 30 1 98%

@pytorch-bot

pytorch-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21751

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 3aac96f with merge base c56e6bf (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 11, 2026
@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor

@pytorchbot label "release notes: qualcomm"

@pytorch-botpytorch-botBot added the release notes: qualcomm Changes to the Qualcomm backend delegate label Aug 11, 2026
@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external
dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

@psiddh

Copy link
Copy Markdown
Contributor

@qti-horodnic Firstly, this is a great refactor, modular, composable, and creating abstractions for all external dependencies. This will also help a lot with unit testing things. In general +1 to this direction. I have a few high-level thoughts, nothing blocking, mostly about what gets frozen here vs. what lands in PRs 5-7.

  1. Per-model transforms and the registry. The loader docstring says per-model graph/weight transforms will be "declared as data on the model registry entry" rather than via adapter subclasses, which is great imo. One thing that might be worth doing before the interfaces settle: today those transforms aren't referenceable yet — convert_linear_to_conv2d is a shared function, but things like the Gemma RMSNorm +1 offset and the partial-RoPE permute are inline in _prepare_model. Could we extract them into named transforms first, then add the transform-list column? Also worth noting one of them is the HF-module to static flat-KV-module swap, which means a transform is Module -> Module rather than an in-place mutation, that may constrain the signature.

  2. Related: LLMModelConfig / @register_llm_model in examples/qualcomm/oss_scripts/llama/ is already a registry (carries repo_id, quant_recipe, num_sharding, convert_weights). Is the plan to extend that one, or introduce a new one? Just want to avoid ending up with two sources of per-model truth.

  3. No "graph set" concept : One weight set can expand into N graphs, and N is a strategy decision: static_llama yields
    prefill / decode / token-embedding / calibrate-only; a single multi-token graph collapses to one. Every adapter here assumes N=1 structurally, and the fan-out , plus the cross-graph encoding copy that's required whenever N>1 — is punted to strategies that don't exist yet, so each will re-derive the same set. A graph-set type (like build_qnn_llm_graphset) is the missing abstraction; N=1 is just its degenerate case. Wdyt ?

  4. Are we replacing llama.py with this new GenAI pipeline, or will both paths exist?

  5. DefaultCompilerAdapter. Small one, the default currently raises NotImplementedError and the working path is "inject your own." Would it make sense to have the proven to_edge_transform_and_lower_to_qnn path be the default (or) following PRs will fill it ?

@qti-horodnic

qti-horodnic commented Aug 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@psiddh
Thanks for taking the time to review and give feedback, I appreciate it. I'll address each one of your comments in a follow-up comment below, but before that let me just provide a brief overview of the project's structure.

Phase 1: The current work, includes PRs 1-7 as outlined in the PR description. Includes only the addition of the skeleton of the new GenAI infrastructure. No existing code paths change behavior, and the skeleton isn't wired into any current entry point. This phase is purely additive and inert by construction, which is why several interfaces here are single-graph / stub-bodied: they're the N=1 degenerate case, with the general form landing in Phase 2.

Phase 2: The next phase will include 4 PRs, divided between me and @DannyYuyang-quic into 2 (roughly) parallel work streams. This phase will include moving legacy code (e.g. llama.py) behind the adapters with a compat shim. The bulk of the logic implementation happens in this phase: multi-graph export, encoding reconciliation, the working compiler adapter, and the device runner.
Note that the existing code will stay exactly where it is in this phase, no code is removed, in order to maintain backwards compatibility.

Phase 3: Cleanup of old code. This will be done after only phase 2 has been completed and all critical code paths have been validated successfully. Nothing is deleted before this phase.

@qti-horodnic

Copy link
Copy Markdown
ContributorAuthor
  1. Agreed. I looked at _prepare_model more closely and there are actually four kinds: state_dict mutations pre-load, one that needs the constructed module (the RoPE permute reads head counts), in-place module mutations, and Module -> Module. Ordering between them is load bearing, so it'll be two lists rather than a flat transforms column. Extracting them as named functions in Phase 2, before anything depends on the shape.

On the HF -> static flat-KV swap: I'd keep that out of the transform list since it's "which class to construct" and is already registry data. Separating the two keeps the Module -> Module constraint from spreading to everything else.

  1. I plan to extend LLMModelConfig. This will be done as part of phase 2 as well.

  2. Agreed, and it's already the frozen interface between the phase 2 work streams.

  3. As mentioned in my comment above, llama.py stays as is through phase 2. It will be removed in phase 3, gated on parity testing the functionality.

  4. Good catch, the docstring is inaccurate. The real implementation against those APIs is in PR6 in this phase, so the default is functional there rather than "inject your own". Fixed the docstring here.

@psiddh

Copy link
Copy Markdown
Contributor

Merging it now (inert for now) , as it unblocks the next few PRs

@psiddh
psiddh merged commit 48974c2 into pytorch:mainAug 17, 2026
184 checks passed
psiddh pushed a commit that referenced this pull request Aug 28, 2026
… quantization strategy implementations (#21899)
## Summary
This PR implements the __model preparation__ and __quantization__
strategy implementations, replacing the `NotImplementedError` stubs with
real logic. Each strategy delegates to injectable adapter interfaces
(from PR4) for testability.
### What's included
#### Strategy implementations (2 files + 1 `__init__` fix):
- `ExecuTorchModelPreparationStrategy`: 5-step flow
- `load_model` → `load_tokenizer` (via `ModelLoaderAdapter`)
- `generate_calibration_data` (via separately-injectable
`CalibrationDataAdapter`)
- Optional tokenizer export for on-device runtime
- Chat template extraction from tokenizer (with `extra_options`
fallback)
- Validates input config (`model_name`, `soc_model` required)
- `ExecuTorchQuantizationStrategy`: Full PT2E single-graph pipeline via
`QuantizerAdapter`
- export → make_quantizer → prepare_pt2e → calibrate → convert_pt2e
- Supports `quant_dtype`, `quant_recipe`, and per-channel options via
`extra_options`
- Handles any `Iterable` as calibration data (lists, DataLoaders,
generators)
- Validates calibration data is non-empty before export
- Warns (does not fail) when `training_data` is provided (QAT deferred)
- `strategies/model_preparation/__init__.py`: adds missing
`ExecuTorchModelPreparationStrategy` import to `__all__`
#### Unit tests:
- `test_executorch_model_preparation_strategy.py`
- `test_executorch_quantization_strategy.py` - `test_default_model_preparation_adapter.py` - `test_default_model_preparation_adapter.py` ### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies: this pr.
- PR 6: Compilation & inference strategy implementations: pending.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/model_preparation/ \
backends/qualcomm/genai_pipeline/tests/strategies/quantization/ \
--cov=backends/qualcomm/genai_pipeline/strategies/model_preparation \
--cov=backends/qualcomm/genai_pipeline/strategies/quantization \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 161 0 32 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all `genai_pipeline` tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 593 9 76 1 99%
```
psiddh pushed a commit that referenced this pull request Aug 29, 2026
…ence strategy implementations (#22284)
## Summary
This PR implements the __compilation__ and __inference__ strategy
implementations, completing the strategy layer. Each strategy delegates
to injectable adapter interfaces for testability.
### What's included
#### Strategy implementations:
- `ExecuTorchCompilationStrategy`: Compiles model to .pte artifacts via
`CompilerAdapter`
- Validates model, example_inputs, soc_model, and backend_type are
present
- Passes `example_inputs` explicitly to the adapter (not via
`extra_options`) — mirrors the PR5 fix for the quantization stage
- Delegates to adapter with example_inputs, compile specs, artifact dir,
soc_model, backend_type
- Filters `context.extra_options` to a compilation-relevant allow-list
- Returns artifact paths and optional `ETRecord`
- `ExecuTorchInferenceStrategy`: Runs on-device inference via
`DeviceRunnerAdapter`
- Validates artifact_paths and adapter are present (no default adapter —
device config is required)
- Push → execute → pull results flow
- Two-step protocol: uses `output_data` from execute if present, falls
back to pulled file paths
- Returns inference results, performance metrics, and optional `ETDump`
#### `DefaultCompilerAdapter`:
- `compile_model` signature finalised — mirrors
`to_edge_transform_and_lower_to_qnn`
argument-for-argument, so per-graph lowering inputs (`compile_specs`,
`dep_table`,
`passes_job`, `constant_methods`) are explicit parameters rather than
`extra_options` keys
- Body deliberately raises `NotImplementedError`: the version this
package needs is
the multi-graph one (graph-name-keyed dicts, single multi-method `.pte`
for weight
sharing), so it lands with the strategy-level fan-out that calls it
rather than
being written single-graph and then replaced. Inject a custom
`CompilerAdapter` for now.
#### Config addition:
- `CompilationInputConfig.example_inputs: Optional[Tuple[Any, ...]]` —
sourced from the model via `ModelLoaderAdapter.get_example_inputs`
#### Orchestrator wiring:
- `genai_pipeline.py` `_run_compilation`: passes
`example_inputs=model_prep_output.example_inputs`
#### Unit tests:
- `test_executorch_compilation_strategy.py` - `test_executorch_inference_strategy.py`
- `test_compilation_input_config.py`
### PR Review Checklist
- All new classes follow single responsibility (one class per file) -
Yes.
- All dependencies are injected via constructor with sensible defaults -
Yes.
- All external calls are behind injectable interfaces - Yes (adapter
pattern).
- Unit tests cover every public method - Yes (100% coverage on strategy
impls).
- Docstrings on all public classes and methods - Yes.
- Type annotations on all function signatures - Yes.
- Logging follows the strategy in the LLD - Yes (info on entry/exit,
debug per step).
### Related PRs
- PR 1: Core data model, engine routing & exceptions:
#20409
- PR 2: Strategy interfaces & stage wrappers:
#20795
- PR 3: Pipeline orchestrator:
#21149
- PR 4: Adapter interfaces, default implementations & dataset providers:
#21751
- PR 5: Model preparation & quantization strategies:
#21899
- PR 6: Compilation & inference strategy implementations: this pr.
- PR 7: Integration & E2E tests: pending.
## Test plan
### Run only tests added in this PR:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py
-v
```
### Run only this PR's tests with coverage:
```
python -m pytest \
backends/qualcomm/genai_pipeline/tests/strategies/compilation/ \
backends/qualcomm/genai_pipeline/tests/strategies/inference/ \
backends/qualcomm/genai_pipeline/tests/configs/test_compilation_input_config.py \
--cov=backends/qualcomm/genai_pipeline/strategies/compilation \
--cov=backends/qualcomm/genai_pipeline/strategies/inference \
--cov=backends/qualcomm/genai_pipeline/configs/compilation_input_config \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------
TOTAL 127 0 16 0 100%
```
### Run all `genai_pipeline` tests:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ -v
```
### Run all tests with coverage:
```
python -m pytest backends/qualcomm/genai_pipeline/tests/ \
--cov=backends/qualcomm/genai_pipeline \
--cov-config=backends/qualcomm/.coveragerc \
--cov-report=term-missing
```
Result:
```
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------------------------------------------------------------------
backends/qualcomm/genai_pipeline/configs/compilation_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/compilation_output_config.py 8 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_input_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/inference_output_config.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_input_config.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/model_preparation_output_config.py 12 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_input_config.py 13 0 0 0 100%
backends/qualcomm/genai_pipeline/configs/quantization_output_config.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/calibration_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/datasets/default_calibration_data_adapter.py 26 0 6 0 100%
backends/qualcomm/genai_pipeline/datasets/default_training_data_adapter.py 14 0 2 0 100%
backends/qualcomm/genai_pipeline/datasets/training_data_adapter.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/engine_proxy.py 20 0 4 0 100%
backends/qualcomm/genai_pipeline/exceptions.py 20 0 6 0 100%
backends/qualcomm/genai_pipeline/genai_pipeline.py 99 7 12 1 93% 190-201
backends/qualcomm/genai_pipeline/pipeline_context.py 52 0 14 0 100%
backends/qualcomm/genai_pipeline/pipeline_stage.py 5 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/compilation_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/inference_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/stages/model_preparation_stage.py 14 2 0 0 86% 30, 37
backends/qualcomm/genai_pipeline/stages/quantization_stage.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compilation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/compiler_adapter.py 11 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/compilation/executorch_compilation_strategy.py 45 0 10 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/device_runner_adapter.py 14 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/executorch_inference_strategy.py 43 0 6 0 100%
backends/qualcomm/genai_pipeline/strategies/inference/inference_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/executorch_model_preparation_strategy.py 68 0 14 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_loader_adapter.py 9 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/model_preparation/model_preparation_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/executorch_quantization_strategy.py 61 0 18 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantization_strategy.py 7 0 0 0 100%
backends/qualcomm/genai_pipeline/strategies/quantization/quantizer_adapter.py 9 0 0 0 100%
----------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 668 9 92 1 99%
```
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.release notes: qualcommChanges to the Qualcomm backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qti-horodnic@psiddh