2025-10-28 21:20:53 +01:00
import torch
import logging
2026-05-03 20:07:21 +02:00
from comfy . cli_args import args
2026-07-10 18:31:20 +08:00
2026-01-05 18:48:58 -08:00
try :
import comfy_kitchen as ck
from comfy_kitchen . tensor import (
QuantizedTensor ,
QuantizedLayout ,
TensorCoreFP8Layout as _CKFp8Layout ,
2026-01-12 19:33:54 -08:00
TensorCoreNVFP4Layout as _CKNvfp4Layout ,
2026-07-09 15:57:09 -07:00
TensorCoreConvRotW4A4Layout as _CKTensorCoreConvRotW4A4Layout ,
2026-06-25 11:23:58 -07:00
TensorWiseINT8Layout as _CKTensorWiseINT8Layout ,
2026-08-07 18:32:57 +03:00
AsymW4A8Int8Layout as _CKAsymW4A8Int8Layout ,
2026-01-05 18:48:58 -08:00
register_layout_op ,
register_layout_class ,
get_layout_class ,
)
_CK_AVAILABLE = True
2026-01-06 19:13:43 -08:00
if torch . version . cuda is None :
ck . registry . disable ( " cuda " )
else :
cuda_version = tuple ( map ( int , str ( torch . version . cuda ) . split ( ' . ' ) ) )
if cuda_version < ( 13 , ) :
ck . registry . disable ( " cuda " )
2026-08-10 20:25:01 -07:00
logging . warning ( " WARNING: You need pytorch with cu130 or higher to use optimized CUDA operations. \n WARNING WARNING WARNING \n If you are on nvidia 20 series and above it is required that you update your pytorch to cu130 or higher. \n " )
2026-01-06 19:13:43 -08:00
2026-09-09 23:14:27 +03:00
# comfy-kitchen picks its accelerated backend on import: the HIP backend registers
# itself on a supported AMD device and takes dispatch priority there, CUDA on NVIDIA.
# Triton is an opt-in override, off by default on every platform.
if args . enable_triton_backend and not args . disable_triton_backend :
2026-05-03 20:07:21 +02:00
try :
import triton
2026-09-09 23:14:27 +03:00
logging . info ( " Found triton %s . Enabling comfy-kitchen triton backend. " , triton . __version__ )
2026-05-03 20:07:21 +02:00
except ImportError as e :
logging . error ( f " Failed to import triton, Error: { e } , the comfy-kitchen triton backend will not be available. " )
ck . registry . disable ( " triton " )
else :
ck . registry . disable ( " triton " )
2026-01-05 18:48:58 -08:00
for k , v in ck . list_backends ( ) . items ( ) :
logging . info ( f " Found comfy_kitchen backend { k } : { v } " )
except ImportError as e :
logging . error ( f " Failed to import comfy_kitchen, Error: { e } , fp8 and fp4 support will not be available. " )
_CK_AVAILABLE = False
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
class QuantizedTensor :
pass
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
class _CKFp8Layout :
pass
2025-10-28 21:20:53 +01:00
2026-01-12 19:33:54 -08:00
class _CKNvfp4Layout :
2026-01-05 18:48:58 -08:00
pass
2025-11-24 23:48:20 -08:00
2026-06-25 11:23:58 -07:00
class _CKTensorWiseINT8Layout :
pass
2026-07-09 15:57:09 -07:00
class _CKTensorCoreConvRotW4A4Layout :
pass
2026-08-07 18:32:57 +03:00
class _CKAsymW4A8Int8Layout :
pass
2026-01-05 18:48:58 -08:00
def register_layout_class ( name , cls ) :
pass
2025-11-24 23:48:20 -08:00
2026-01-05 18:48:58 -08:00
def get_layout_class ( name ) :
return None
2025-10-28 21:20:53 +01:00
2026-03-15 00:36:29 +02:00
_CK_MXFP8_AVAILABLE = False
if _CK_AVAILABLE :
try :
from comfy_kitchen . tensor import TensorCoreMXFP8Layout as _CKMxfp8Layout
_CK_MXFP8_AVAILABLE = True
except ImportError :
logging . warning ( " comfy_kitchen does not support MXFP8, please update comfy_kitchen. " )
if not _CK_MXFP8_AVAILABLE :
class _CKMxfp8Layout :
pass
2026-01-05 18:48:58 -08:00
import comfy . float
2025-12-05 11:35:42 -08:00
2025-10-28 21:20:53 +01:00
# ==============================================================================
2026-01-05 18:48:58 -08:00
# FP8 Layouts with Comfy-Specific Extensions
2025-10-28 21:20:53 +01:00
# ==============================================================================
2026-01-05 18:48:58 -08:00
class _TensorCoreFP8LayoutBase ( _CKFp8Layout ) :
FP8_DTYPE = None # Must be overridden in subclass
2025-10-28 21:20:53 +01:00
@classmethod
2026-01-05 18:48:58 -08:00
def quantize ( cls , tensor , scale = None , stochastic_rounding = 0 , inplace_ops = False ) :
if cls . FP8_DTYPE is None :
raise NotImplementedError ( f " { cls . __name__ } must define FP8_DTYPE " )
2025-10-28 21:20:53 +01:00
orig_dtype = tensor . dtype
2026-01-05 18:48:58 -08:00
orig_shape = tuple ( tensor . shape )
2025-10-28 21:20:53 +01:00
2025-12-05 11:35:42 -08:00
if isinstance ( scale , str ) and scale == " recalculate " :
2026-01-05 18:48:58 -08:00
scale = torch . amax ( tensor . abs ( ) ) . to ( dtype = torch . float32 ) / torch . finfo ( cls . FP8_DTYPE ) . max
2025-12-09 14:03:21 -08:00
if tensor . dtype not in [ torch . float32 , torch . bfloat16 ] : # Prevent scale from being too small
tensor_info = torch . finfo ( tensor . dtype )
scale = ( 1.0 / torch . clamp ( ( 1.0 / scale ) , min = tensor_info . min , max = tensor_info . max ) )
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
if scale is None :
scale = torch . ones ( ( ) , device = tensor . device , dtype = torch . float32 )
if not isinstance ( scale , torch . Tensor ) :
scale = torch . tensor ( scale , device = tensor . device , dtype = torch . float32 )
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
if stochastic_rounding > 0 :
2025-12-05 11:35:42 -08:00
if inplace_ops :
tensor * = ( 1.0 / scale ) . to ( tensor . dtype )
else :
tensor = tensor * ( 1.0 / scale ) . to ( tensor . dtype )
2026-01-05 18:48:58 -08:00
qdata = comfy . float . stochastic_rounding ( tensor , dtype = cls . FP8_DTYPE , seed = stochastic_rounding )
2025-11-25 21:07:58 -08:00
else :
2026-01-05 18:48:58 -08:00
qdata = ck . quantize_per_tensor_fp8 ( tensor , scale , cls . FP8_DTYPE )
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
params = cls . Params ( scale = scale . float ( ) , orig_dtype = orig_dtype , orig_shape = orig_shape )
return qdata , params
2025-10-28 21:20:53 +01:00
2026-03-15 00:36:29 +02:00
class TensorCoreMXFP8Layout ( _CKMxfp8Layout ) :
@classmethod
def quantize ( cls , tensor , scale = None , stochastic_rounding = 0 , inplace_ops = False ) :
if tensor . dim ( ) != 2 :
raise ValueError ( f " MXFP8 requires 2D tensor, got { tensor . dim ( ) } D " )
orig_dtype = tensor . dtype
orig_shape = tuple ( tensor . shape )
padded_shape = cls . get_padded_shape ( orig_shape )
needs_padding = padded_shape != orig_shape
if stochastic_rounding > 0 :
qdata , block_scale = comfy . float . stochastic_round_quantize_mxfp8_by_block ( tensor , pad_32x = needs_padding , seed = stochastic_rounding )
else :
qdata , block_scale = ck . quantize_mxfp8 ( tensor , pad_32x = needs_padding )
params = cls . Params (
scale = block_scale ,
orig_dtype = orig_dtype ,
orig_shape = orig_shape ,
)
return qdata , params
2026-01-12 19:33:54 -08:00
class TensorCoreNVFP4Layout ( _CKNvfp4Layout ) :
@classmethod
def quantize ( cls , tensor , scale = None , stochastic_rounding = 0 , inplace_ops = False ) :
if tensor . dim ( ) != 2 :
raise ValueError ( f " NVFP4 requires 2D tensor, got { tensor . dim ( ) } D " )
orig_dtype = tensor . dtype
orig_shape = tuple ( tensor . shape )
if scale is None or ( isinstance ( scale , str ) and scale == " recalculate " ) :
scale = torch . amax ( tensor . abs ( ) ) / ( ck . float_utils . F8_E4M3_MAX * ck . float_utils . F4_E2M1_MAX )
if not isinstance ( scale , torch . Tensor ) :
scale = torch . tensor ( scale )
scale = scale . to ( device = tensor . device , dtype = torch . float32 )
padded_shape = cls . get_padded_shape ( orig_shape )
needs_padding = padded_shape != orig_shape
if stochastic_rounding > 0 :
2026-01-13 21:49:38 -08:00
qdata , block_scale = comfy . float . stochastic_round_quantize_nvfp4_by_block ( tensor , scale , pad_16x = needs_padding , seed = stochastic_rounding )
2026-01-12 19:33:54 -08:00
else :
qdata , block_scale = ck . quantize_nvfp4 ( tensor , scale , pad_16x = needs_padding )
params = cls . Params (
scale = scale ,
orig_dtype = orig_dtype ,
orig_shape = orig_shape ,
block_scale = block_scale ,
)
return qdata , params
2026-01-05 18:48:58 -08:00
class TensorCoreFP8E4M3Layout ( _TensorCoreFP8LayoutBase ) :
FP8_DTYPE = torch . float8_e4m3fn
2025-10-28 21:20:53 +01:00
2025-10-31 21:25:17 -07:00
2026-01-05 18:48:58 -08:00
class TensorCoreFP8E5M2Layout ( _TensorCoreFP8LayoutBase ) :
FP8_DTYPE = torch . float8_e5m2
2025-10-31 21:25:17 -07:00
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
# Backward compatibility alias - default to E4M3
TensorCoreFP8Layout = TensorCoreFP8E4M3Layout
2026-06-25 11:23:58 -07:00
TensorWiseINT8Layout = _CKTensorWiseINT8Layout
2026-07-09 15:57:09 -07:00
TensorCoreConvRotW4A4Layout = _CKTensorCoreConvRotW4A4Layout
2026-08-07 18:32:57 +03:00
AsymW4A8Int8Layout = _CKAsymW4A8Int8Layout
2025-10-28 21:20:53 +01:00
2026-01-05 18:48:58 -08:00
# ==============================================================================
# Registry
# ==============================================================================
2025-11-03 16:22:10 -08:00
2026-01-05 18:48:58 -08:00
register_layout_class ( " TensorCoreFP8Layout " , TensorCoreFP8Layout )
register_layout_class ( " TensorCoreFP8E4M3Layout " , TensorCoreFP8E4M3Layout )
register_layout_class ( " TensorCoreFP8E5M2Layout " , TensorCoreFP8E5M2Layout )
register_layout_class ( " TensorCoreNVFP4Layout " , TensorCoreNVFP4Layout )
2026-06-25 11:23:58 -07:00
register_layout_class ( " TensorWiseINT8Layout " , _CKTensorWiseINT8Layout )
2026-07-09 15:57:09 -07:00
register_layout_class ( " TensorCoreConvRotW4A4Layout " , _CKTensorCoreConvRotW4A4Layout )
2026-03-15 00:36:29 +02:00
if _CK_MXFP8_AVAILABLE :
register_layout_class ( " TensorCoreMXFP8Layout " , TensorCoreMXFP8Layout )
2026-08-07 18:32:57 +03:00
register_layout_class ( " AsymW4A8Int8Layout " , _CKAsymW4A8Int8Layout )
2025-11-03 16:22:10 -08:00
2026-01-05 18:48:58 -08:00
QUANT_ALGOS = {
" float8_e4m3fn " : {
" storage_t " : torch . float8_e4m3fn ,
" parameters " : { " weight_scale " , " input_scale " } ,
" comfy_tensor_layout " : " TensorCoreFP8E4M3Layout " ,
} ,
" float8_e5m2 " : {
" storage_t " : torch . float8_e5m2 ,
" parameters " : { " weight_scale " , " input_scale " } ,
" comfy_tensor_layout " : " TensorCoreFP8E5M2Layout " ,
} ,
" nvfp4 " : {
" storage_t " : torch . uint8 ,
2026-08-03 05:28:29 +03:00
" parameters " : { " weight_scale " , " weight_scale_2 " , " input_scale " , " pre_quant_scale " } ,
2026-01-05 18:48:58 -08:00
" comfy_tensor_layout " : " TensorCoreNVFP4Layout " ,
" group_size " : 16 ,
} ,
}
2025-11-03 19:14:20 -08:00
2026-03-15 00:36:29 +02:00
if _CK_MXFP8_AVAILABLE :
QUANT_ALGOS [ " mxfp8 " ] = {
" storage_t " : torch . float8_e4m3fn ,
" parameters " : { " weight_scale " , " input_scale " } ,
" comfy_tensor_layout " : " TensorCoreMXFP8Layout " ,
" group_size " : 32 ,
}
2026-06-25 11:23:58 -07:00
QUANT_ALGOS [ " int8_tensorwise " ] = {
" storage_t " : torch . int8 ,
" parameters " : { " weight_scale " } ,
" comfy_tensor_layout " : " TensorWiseINT8Layout " ,
" quantize_input " : False ,
}
2026-07-09 15:57:09 -07:00
QUANT_ALGOS [ " convrot_w4a4 " ] = {
" storage_t " : torch . int8 ,
" parameters " : { " weight_scale " } ,
" comfy_tensor_layout " : " TensorCoreConvRotW4A4Layout " ,
" quantize_input " : False ,
}
2026-08-07 18:32:57 +03:00
QUANT_ALGOS [ " asym_w4a8_int8 " ] = {
" storage_t " : torch . int8 ,
" parameters " : { " weight_scale " } ,
" comfy_tensor_layout " : " AsymW4A8Int8Layout " ,
" quantize_input " : False ,
}
2025-11-03 19:14:20 -08:00
2026-01-05 18:48:58 -08:00
# ==============================================================================
# Re-exports for backward compatibility
# ==============================================================================
2025-11-03 19:14:20 -08:00
2026-01-05 18:48:58 -08:00
__all__ = [
" QuantizedTensor " ,
" QuantizedLayout " ,
" TensorCoreFP8Layout " ,
" TensorCoreFP8E4M3Layout " ,
" TensorCoreFP8E5M2Layout " ,
" TensorCoreNVFP4Layout " ,
2026-07-09 15:57:09 -07:00
" TensorCoreConvRotW4A4Layout " ,
2026-06-25 11:23:58 -07:00
" TensorWiseINT8Layout " ,
2026-08-07 18:32:57 +03:00
" AsymW4A8Int8Layout " ,
2026-01-05 18:48:58 -08:00
" QUANT_ALGOS " ,
" register_layout_op " ,
]