@@ -63,15 +63,17 @@ def _has_float_lut(config: ParametrizedPalettConfigs) -> bool:
6363 return config .lut_qspec is not None and config .lut_qspec .dtype .is_floating_point
6464
6565
66- def _assert_coreml_rejects_unsupported_lut (
66+ def _assert_coreml_rejects (
6767 model : torch .nn .Module ,
6868 input_data : torch .Tensor ,
6969 config : KMeansPalettizerConfig ,
7070) -> None :
71- """Assert finalize(CoreML) rejects an unsupported LUT dtype .
71+ """Assert finalize(CoreML) rejects an unsupported palettization config .
7272
73- CoreML/MIL does not support FP or INT2 LUT quantization, so finalize must raise
74- rather than emit an invalid model.
73+ CoreML/MIL rejects certain LUT dtypes and certain combinations of
74+ palettization features (vector palettization, LUT quantization,
75+ per-channel scale), so finalize must raise rather than emit an invalid
76+ model.
7577 """
7678 model .eval ()
7779 palettizer = KMeansPalettizer (model , config )
@@ -95,32 +97,17 @@ def _skip_heavy_mnist_configs(config: ParametrizedPalettConfigs) -> None:
9597 pytest .skip (f"MNIST only tests lut_qspec with int8 and float8_e4m3fn, got { dtype } " )
9698
9799
98- def _skip_unsupported_mil_configs (
99- backend : ExportBackend ,
100- config : ParametrizedPalettConfigs ,
101- ) -> None :
102- """Skip CoreML configs with unsupported feature combinations."""
103- if backend != ExportBackend .CoreML :
104- return
105-
100+ def _has_unsupported_mil_combo (config : ParametrizedPalettConfigs ) -> bool :
101+ """Whether the config combines >=2 of {vector palettization, LUT
102+ quantization, per-channel scale} -- verified to be the exact set
103+ CoreML/MIL cannot export: any single one of these works in isolation, but
104+ combining two or more fails with a rank-mismatch, missing-vector_axis, or
105+ LUT-divisibility error depending on the pair.
106+ """
106107 is_vector = config .cluster_dim > 1
107108 has_lut_quant = config .lut_qspec is not None
108109 has_pcs = config .enable_per_channel_scale
109-
110- # Vector palettization + LUT quantization
111- if is_vector and has_lut_quant :
112- # TODO: add CoreML export support for palettization combos.
113- pytest .skip ("CoreML export not supported for vector palettization + LUT quantization." )
114-
115- # Vector palettization + per-channel scale
116- if is_vector and has_pcs :
117- # TODO: add CoreML export support for palettization combos.
118- pytest .skip ("CoreML export not supported for vector palettization + per-channel scale." )
119-
120- # LUT quantization + per-channel scale
121- if has_lut_quant and has_pcs :
122- # TODO: add CoreML export support for palettization combos.
123- pytest .skip ("CoreML export not supported for LUT quantization + per-channel scale." )
110+ return sum ([is_vector , has_lut_quant , has_pcs ]) >= 2
124111
125112
126113@pytest .mark .parametrize ("backend" , [ExportBackend .CoreML , ExportBackend .CoreAI ])
@@ -134,12 +121,13 @@ def test_simple_model_export(
134121 config = parametrized_palett_config .config
135122 granularity = parametrized_palett_config .granularity
136123
137- if backend == ExportBackend .CoreML and _has_float_lut (parametrized_palett_config ):
138- _assert_coreml_rejects_unsupported_lut (simple_conv_linear_model , simple_model_input , config )
124+ if backend == ExportBackend .CoreML and (
125+ _has_float_lut (parametrized_palett_config )
126+ or _has_unsupported_mil_combo (parametrized_palett_config )
127+ ):
128+ _assert_coreml_rejects (simple_conv_linear_model , simple_model_input , config )
139129 return
140130
141- _skip_unsupported_mil_configs (backend , parametrized_palett_config )
142-
143131 if (
144132 backend == ExportBackend .CoreML
145133 and parametrized_palett_config .cluster_dim > 1
@@ -175,12 +163,13 @@ def test_mnist_export(
175163 config = parametrized_palett_config .config
176164 granularity = parametrized_palett_config .granularity
177165
178- if backend == ExportBackend .CoreML and _has_float_lut (parametrized_palett_config ):
179- _assert_coreml_rejects_unsupported_lut (custom_test_mnist_model , mnist_example_input , config )
166+ if backend == ExportBackend .CoreML and (
167+ _has_float_lut (parametrized_palett_config )
168+ or _has_unsupported_mil_combo (parametrized_palett_config )
169+ ):
170+ _assert_coreml_rejects (custom_test_mnist_model , mnist_example_input , config )
180171 return
181172
182- _skip_unsupported_mil_configs (backend , parametrized_palett_config )
183-
184173 # The MNIST model has 6 weight-bearing layers (conv1, conv2, conv_transpose1,
185174 # conv_transpose2, dense1, dense2). For axis=1 with group_size=2, conv1's
186175 # axis-1 (in_channels=1) is not divisible, so palettization is skipped there.
0 commit comments