def get_quant_model(
        self, model: HfModelHandler, config: type[BasePassConfig], output_model_path: str
    ) -> tuple[HfModelHandler, "PreTrainedModel", dict, list[str]]:
        """Get the model handler, LoRA model for QLoRA fine-tuning.

        :param model: The input model handler.
        :param config: The config for the pass run.
        :param output_model_path: The path to save the output model to.
        :return: The new model handler, LoRA model, quantization config and list of quantized modules.
        """
        import torch

        # get the original base model
        pytorch_model = self.load_base_pytorch_model(model, config)
        # find all modules that will be quantized, all linears except the lm_head
        quantized_modules = [
            module for module in find_submodules(pytorch_model, torch.nn.Linear) if module != "lm_head"
        ]

        # get loftq initialized lora model
        logger.debug("Initializing LoRA with LoftQ")
        config.target_modules = quantized_modules
        pytorch_model = self.init_adapters(pytorch_model, config, task=model.task, use_loftq=True)

        output_model_path = Path(output_model_path)

        # save the loftq initialized adapter weights
        loftq_init_adapter_path = output_model_path / "loftq_init_adapter"
        loftq_init_adapter_path.mkdir(parents=True, exist_ok=True)
        # change adapter config since we don't want to apply loftq again
        pytorch_model.peft_config["default"].init_lora_weights = True
        pytorch_model.save_pretrained(loftq_init_adapter_path)

        # unload adapter and get the base model with new weights
        pytorch_model: PreTrainedModel = pytorch_model.unload()

        # save the new master weights
        new_master_weights_path = output_model_path / "model"
        new_master_weights_path.mkdir(parents=True, exist_ok=True)
        pytorch_model.save_pretrained(new_master_weights_path)
        model.save_metadata(new_master_weights_path)

        del pytorch_model

        # create new model handler
        new_model_handler = deepcopy(model)
        # update the model path in new model handler
        new_model_handler.set_resource("model_path", new_master_weights_path)

        # get the quantized base model
        bnb_quant_config = {
            "quantization_method": "bitsandbytes",
            "quantization_config": {
                "load_in_4bit": True,
                "bnb_4bit_compute_dtype": self.get_torch_dtype(config.compute_dtype or config.torch_dtype),
                "bnb_4bit_use_double_quant": False,
                "bnb_4bit_quant_type": "nf4",
            },
        }
        pytorch_model = self.load_base_pytorch_model(new_model_handler, config, **bnb_quant_config)

        # enable lora fine-tuning with the loftq initialized adapter weights
        pytorch_model = self.enable_lora(
            pytorch_model, config, new_model_handler.task, adapter_path=loftq_init_adapter_path
        )

        return new_model_handler, pytorch_model, bnb_quant_config, quantized_modules
Get the model handler, LoRA model for QLoRA fine-tuning.

        :param model: The input model handler.
        :param config: The config for the pass run.
        :param output_model_path: The path to save the output model to.
        :return: The new model handler, LoRA model, quantization config and list of quantized modules.
        
olive/passes/pytorch/lora.py