def quantize(self, node: onnx.NodeProto, node_initializers: list[onnx.TensorProto]) -> list[onnx.NodeProto]:
        """Quantize the weight of the target node and return the new nodes.

        Target node:        QOperator node:
        MatMul              MatMulNBits
        If the node is target node with fp32 or fp16 const weight, quantize the weight to int4 and
        return the new nodes and initializers.
        """
        logger.debug("start to quantize %s ...", node.name)
        b_pb = node_initializers[0]

        b_array = onnx.numpy_helper.to_array(b_pb)
        if len(b_array.shape) != 2:
            logger.debug("MatMul weight is not 2D. Skip to quantize")
            return [node]  # can only process 2-D matrix
        b_array_torch = torch.from_numpy(b_array)
        if torch.cuda.is_available():
            b_array_torch = b_array_torch.cuda()
        quant_weight_torch, scales_torch, zero_points_torch = self.quantize_internal(
            b_array_torch.T, group_size=self.block_size
        )
        quant_weight_torch = quant_weight_torch.contiguous()
        scales_torch = scales_torch.contiguous()
        zero_points_torch = zero_points_torch.contiguous()

        packed_torch = torch.zeros(
            (quant_weight_torch.shape[0], quant_weight_torch.shape[1] // 2),
            dtype=torch.uint8,
            device=quant_weight_torch.device,
        )
        self.pack_on_row_fast_248bit(packed_torch, quant_weight_torch)
        scales = scales_torch.cpu().numpy()
        zero_points = zero_points_torch.cpu().numpy()
        # reshape to the predefined shape in MatmulNbits
        scales = scales.reshape(-1)
        zero_points = zero_points.reshape(-1)
        rows, cols = b_array_torch.shape
        block_size = self.block_size
        blob_size = block_size // 2
        k_blocks = (rows + block_size - 1) // block_size
        packed_torch = packed_torch.reshape(cols, k_blocks, blob_size)

        b_quant = onnx.numpy_helper.from_array(packed_torch.cpu().numpy())
        b_quant.name = b_pb.name + "_Q4"
        scales_tensor = onnx.numpy_helper.from_array(scales)
        scales_tensor.name = b_pb.name + "_scales"
        zp_tensor = onnx.numpy_helper.from_array(zero_points)
        zp_tensor.name = b_pb.name + "_zero_points"

        input_names = [node.input[0], b_quant.name, scales_tensor.name, zp_tensor.name]
        initializers = [b_quant, scales_tensor, zp_tensor]

        kwargs = {}
        rows, cols = b_array.shape
        kwargs["K"] = rows
        kwargs["N"] = cols
        kwargs["bits"] = 4  # 4 bits
        kwargs["block_size"] = self.block_size

        new_output = node.output[0] + "_Q4"

        matmul_q4_node = onnx.helper.make_node(
            str(OpType.MatMulNBits),
            inputs=input_names,
            outputs=[new_output],
            name=node.name + "_Q4" if node.name else "",
            domain=MSFT_DOMAIN,
            **kwargs,
        )

        logger.debug("complete quantization of %s ...", node.name)

        return [matmul_q4_node], initializers
Quantize the weight of the target node and return the new nodes.

        Target node:        QOperator node:
        MatMul              MatMulNBits
        If the node is target node with fp32 or fp16 const weight, quantize the weight to int4 and
        return the new nodes and initializers.
        
olive/passes/onnx/hqq_quantization.py