Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions src/coreai_opt/quantization/_graph/_prepare_for_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,11 @@ def _get_weight_input_names(

Returns:
Tuple of (module_name, param_name)
- module_name: e.g., "conv", "layer1.0"
- module_name: e.g., "conv", "layer1.0", or "" for a root-module parameter
- param_name: e.g., "weight", "bias"

Raises:
ValueError: If node is not a weight quantization node
ValueError: If weight target path is invalid

"""
if not _is_weight_fake_quant(fake_quant_node, module):
Expand All @@ -159,13 +158,10 @@ def _get_weight_input_names(
# Extract module and parameter name from target path
# e.g., "conv.weight" -> ("conv", "weight")
# e.g., "layer1.0.weight" -> ("layer1.0", "weight")
# e.g., "weight" -> ("", "weight") for a parameter on the root module
target_path = str(input_node.target)
last_dot_idx = target_path.rfind(".")
if last_dot_idx == -1:
msg = f"Invalid weight target path: {target_path}"
raise ValueError(msg)

module_name = target_path[:last_dot_idx]
module_name = target_path[:last_dot_idx] if last_dot_idx != -1 else ""
param_name = target_path[last_dot_idx + 1 :]

return module_name, param_name
Expand Down
60 changes: 60 additions & 0 deletions tests/export/test_pt2e_weight_only_mil_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,63 @@ def test_simple_model_weight_only_mil_export(
export_backend=backend,
prepared_model_output=prepared_model_output,
)


@pytest.mark.parametrize("dtype", ["int8", "uint8"])
@pytest.mark.parametrize(
"granularity",
[
PerTensorGranularity(),
PerChannelGranularity(axis=0),
PerBlockGranularity(axis=0, block_size=2),
],
)
def test_root_module_weight_only_mil_export(
dtype: str,
granularity: PerTensorGranularity | PerChannelGranularity | PerBlockGranularity,
) -> None:
"""Test weight-only quantization export to CoreML for a root-module weight.

A bare ``nn.Linear`` owns its weight directly, so the fake-quant input target is the
dot-less ``"weight"`` rather than ``"<submodule>.weight"``. The graph CoreML export
path rejected that with ``Invalid weight target path: weight`` instead of resolving it
to the root module.
"""
model = torch.nn.Linear(8, 4)
model.eval()
model_input = torch.randn(2, 8)

config = QuantizerConfig(
global_config=ModuleQuantizerConfig(
op_state_spec={
"weight": QuantizationSpec(
dtype=dtype,
qscheme=QuantizationScheme.SYMMETRIC,
granularity=granularity,
fake_quantize_cls="default",
qparam_calculator_cls="default",
range_calculator_cls="minmax",
),
},
op_input_spec=None,
op_output_spec=None,
),
)

quantizer = Quantizer(model, config)
prepared_model = quantizer.prepare((model_input,))

with torch.no_grad():
prepared_model_output = prepared_model(model_input)

finalized_model = quantizer.finalize(backend=ExportBackend.CoreML)

export_utils.convert_and_verify(
finalized_model=finalized_model,
input_data=model_input,
expected_ops={
"constexpr_blockwise_shift_scale": 1,
},
export_backend=ExportBackend.CoreML,
prepared_model_output=prepared_model_output,
)