|
11 | 11 | from coreai_opt._utils.torch_utils import is_tensor_on_cpu |
12 | 12 | from coreai_opt.common import CoreMLExportError, ExportBackend |
13 | 13 | from coreai_opt.config.spec import CompressionTargetTensor |
| 14 | +from coreai_opt.quantization.spec import QuantizationSpec |
14 | 15 | from coreai_opt.quantization.spec.granularity import PerTensorGranularity, QuantizationGranularity |
15 | 16 |
|
16 | 17 | COREML_SUPPORTED_WEIGHT_DTYPES: frozenset[torch.dtype] = frozenset( |
@@ -78,6 +79,52 @@ def validate_coreml_compatibility( |
78 | 79 | raise CoreMLExportError.from_config(granularity, context) |
79 | 80 |
|
80 | 81 |
|
| 82 | +def validate_coreml_palettization_compatibility( |
| 83 | + cluster_dim: int, |
| 84 | + lut_qspec: QuantizationSpec | None, |
| 85 | + enable_per_channel_scale: bool, |
| 86 | + context: str, |
| 87 | +) -> None: |
| 88 | + """Raise CoreMLExportError if this palettization config isn't CoreML-exportable. |
| 89 | +
|
| 90 | + Checks the LUT dtype (delegating to validate_coreml_compatibility) and |
| 91 | + whether the config combines multiple features CoreML/MIL export cannot yet |
| 92 | + fuse into a single compatible op chain: CoreML export supports at most one |
| 93 | + of {vector palettization, LUT quantization, per-channel scale} at a time; |
| 94 | + combining any two hits an unsupported CoreML/MIL op configuration |
| 95 | + (mismatched tensor ranks, or `lut_to_dense` divisibility errors). |
| 96 | +
|
| 97 | + Args: |
| 98 | + cluster_dim (int): Palettization cluster dimension; > 1 indicates |
| 99 | + vector palettization. |
| 100 | + lut_qspec (QuantizationSpec | None): LUT quantization spec, or None if |
| 101 | + the LUT is not quantized. |
| 102 | + enable_per_channel_scale (bool): Whether per-channel scaling is enabled. |
| 103 | + context (str): Human-readable description of what's being checked. |
| 104 | +
|
| 105 | + Raises: |
| 106 | + CoreMLExportError: If the LUT dtype isn't supported, or if two or more |
| 107 | + of the three features above are combined. |
| 108 | + """ |
| 109 | + if lut_qspec is not None: |
| 110 | + validate_coreml_compatibility( |
| 111 | + CompressionTargetTensor.LUT, lut_qspec.dtype, f"LUT of {context}" |
| 112 | + ) |
| 113 | + |
| 114 | + active_features = [] |
| 115 | + if cluster_dim > 1: |
| 116 | + active_features.append("vector palettization") |
| 117 | + if lut_qspec is not None: |
| 118 | + active_features.append("LUT quantization") |
| 119 | + if enable_per_channel_scale: |
| 120 | + active_features.append("per-channel scale") |
| 121 | + |
| 122 | + if len(active_features) >= 2: |
| 123 | + raise CoreMLExportError( |
| 124 | + f"CoreML export does not support {' + '.join(active_features)} on {context}." |
| 125 | + ) |
| 126 | + |
| 127 | + |
81 | 128 | def validate_mmap_backend_and_device( |
82 | 129 | model: torch.nn.Module, |
83 | 130 | backend: ExportBackend, |
|
0 commit comments