Repository: huggingface/peft

Issue:
Problem with merging AdaLoRA adapters
### System Info

```
peft @ git+https://github.com/huggingface/peft@c8974c5880b28a913e35f050e82402e34d181c63
accelerate==0.29.2
transformers==4.39.3
```

Ubuntu 22.04.4 LTS
Python 3.11.9

### Who can help?

@BenjaminBossan @pacman100


### Information

- [ ] The official example scripts
- [X] My own modified scripts

### Tasks

- [ ] An officially supported task in the `examples` folder
- [X] My own task or dataset (give details below)

### Reproduction

I am trying to merge two AdaLoRA checkpoints of the same model and have written this script for it:

```python
from peft import PeftConfig, PeftModel
from transformers import AutoModelForSeq2SeqLM

model_id = "Salesforce/codet5-small"
config = PeftConfig.from_pretrained("checkpoint-6621")
model = AutoModelForSeq2SeqLM.from_pretrained(config.base_model_name_or_path, device_map="auto")

model = PeftModel.from_pretrained(model, "checkpoint-6621", adapter_name="checkpoint-6621")
model.load_adapter("checkpoint-13242", adapter_name="checkpoint-13242")

adapters = ["checkpoint-6621", "checkpoint-13242"]
weights = [1.0, 1.0]
adapter_name = "merged"
density = 0.2
model.add_weighted_adapter(adapters, weights, adapter_name, combination_type="ties", density=density)

model.set_adapter("merged")
print(model.active_adapters)
```

When I run the script, I get this error:

```
Traceback (most recent call last):
  File "merge.py", line 15, in <module>
    model.add_weighted_adapter(adapters, weights, adapter_name, combination_type="ties", density=density)
  File ".venv/lib/python3.11/site-packages/peft/tuners/lora/model.py", line 579, in add_weighted_adapter
    combination_type, new_rank, new_target_modules = self._check_add_weighted_adapter(
                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.11/site-packages/peft/tuners/lora/model.py", line 516, in _check_add_weighted_adapter
    raise TypeError(f"Invalid type {target_module_types[0]} found in target_modules")
TypeError: Invalid type <class 'list'> found in target_modules
```

The given error is similar to this issue: https://github.com/huggingface/peft/issues/1045 

### Expected behavior

It is expected that the adapters merge successfully. The same script works for LoRA adapters, resulting in a merged adapter.


Discussion:
The error you encounter is indeed a bug, which can be fixed by adding the lines:

```python
model.peft_config["checkpoint-6621"].target_modules = set(model.peft_config["checkpoint-6621"].target_modules)
model.peft_config["checkpoint-13242"].target_modules = set(model.peft_config["checkpoint-13242"].target_modules)
```

after `model.load_adapter("checkpoint-13242", adapter_name="checkpoint-13242")`.

However, even with this workaround, this won't work because `add_weighted_adapter` is only supported for LoRA, not AdaLoRA.

Resolving patch:
diff --git a/src/peft/tuners/adalora/config.py b/src/peft/tuners/adalora/config.py
--- a/src/peft/tuners/adalora/config.py
+++ b/src/peft/tuners/adalora/config.py
@@ -50,3 +50,20 @@ class AdaLoraConfig(LoraConfig):
 
     def __post_init__(self):
         self.peft_type = PeftType.ADALORA
+
+        if self.use_dora:
+            raise ValueError(f"{self.peft_type} does not support DoRA.")
+
+        if self.loftq_config:
+            raise ValueError(f"{self.peft_type} does not support LOFTQ.")
+
+        self.target_modules = (
+            set(self.target_modules) if isinstance(self.target_modules, list) else self.target_modules
+        )
+        # if target_modules is a regex expression, then layers_to_transform should be None
+        if isinstance(self.target_modules, str) and self.layers_to_transform is not None:
+            raise ValueError("`layers_to_transform` cannot be used when `target_modules` is a str.")
+
+        # if target_modules is a regex expression, then layers_pattern should be None
+        if isinstance(self.target_modules, str) and self.layers_pattern is not None:
+            raise ValueError("`layers_pattern` cannot be used when `target_modules` is a str.")
diff --git a/src/peft/tuners/adalora/model.py b/src/peft/tuners/adalora/model.py
--- a/src/peft/tuners/adalora/model.py
+++ b/src/peft/tuners/adalora/model.py
@@ -349,3 +349,7 @@ def update_and_allocate(self, global_step):
         # Pass the function and do forward propagation
         else:
             return None
+
+    def add_weighted_adapter(self, *args, **kwargs):
+        """This method is not supported for AdaLoRA, use LoRA instead."""
+        raise TypeError(f"{self.__class__.__name__} does not support add_weighted_adapter method.")


Test patch:
diff --git a/tests/test_initialization.py b/tests/test_initialization.py
--- a/tests/test_initialization.py
+++ b/tests/test_initialization.py
@@ -19,11 +19,11 @@
 from scipy import stats
 from torch import nn
 
-from peft import LoraConfig, PromptTuningConfig, VeraConfig, get_peft_model
+from peft import AdaLoraConfig, LoraConfig, PromptTuningConfig, VeraConfig, get_peft_model
 from peft.utils import infer_device
 
 
-class TestInitialization:
+class TestLoraInitialization:
     """Test class to check the initialization of adapters."""
 
     torch_device = infer_device()
@@ -253,7 +253,7 @@ def test_lora_scaling_default(self):
         assert model.embed.scaling["default"] == expected_scaling
         assert model.conv2d.scaling["default"] == expected_scaling
 
-    def test_rslora_scaling(self):
+    def test_lora_rslora_scaling(self):
         # default is True
         torch.manual_seed(0)
 
@@ -296,7 +296,7 @@ def test_lora_default_scaling_pattern(self):
         assert model.embed.scaling["default"] == expected_scaling["embed"]
         assert model.conv2d.scaling["default"] == expected_scaling["conv2d"]
 
-    def test_rslora_scaling_pattern(self):
+    def test_lora_rslora_scaling_pattern(self):
         # default is True
         torch.manual_seed(0)
 
@@ -323,7 +323,7 @@ def test_rslora_scaling_pattern(self):
         assert model.embed.scaling["default"] == expected_scaling["embed"]
         assert model.conv2d.scaling["default"] == expected_scaling["conv2d"]
 
-    def test_use_dora_linear(self, data):
+    def test_lora_use_dora_linear(self, data):
         # check that dora is a no-op when initialized
         torch.manual_seed(0)
         model = self.get_model()
@@ -340,7 +340,7 @@ def test_use_dora_linear(self, data):
         assert torch.allclose(output_base, output_disabled)
         assert torch.allclose(output_base, output_dora)
 
-    def test_use_dora_linear_init_false(self, data):
+    def test_lora_use_dora_linear_init_false(self, data):
         # with init_lora_weights=False, dora should not be a no-op
         torch.manual_seed(0)
         model = self.get_model()
@@ -357,11 +357,45 @@ def test_use_dora_linear_init_false(self, data):
         assert torch.allclose(output_base, output_disabled)
         assert not torch.allclose(output_base, output_dora)
 
-    def test_use_dora_with_megatron_core_raises(self):
+    def test_lora_use_dora_with_megatron_core_raises(self):
         megatron_config = {"does-not": "matter-here"}
         with pytest.raises(ValueError, match="DoRA does not support megatron_core"):
             LoraConfig(target_modules=["linear"], use_dora=True, megatron_config=megatron_config)
 
+
+class TestAdaLoraInitialization:
+    def test_adalora_target_modules_set(self):
+        config = AdaLoraConfig(target_modules=["linear", "embed", "conv2d"])
+        assert config.target_modules == {"linear", "embed", "conv2d"}
+
+    def test_adalora_use_dora_raises(self):
+        with pytest.raises(ValueError, match="ADALORA does not support DoRA"):
+            AdaLoraConfig(use_dora=True)
+
+    def test_adalora_loftq_config_raises(self):
+        with pytest.raises(ValueError, match="ADALORA does not support LOFTQ"):
+            AdaLoraConfig(loftq_config={"loftq": "config"})
+
+
+class TestPromptTuningInitialization:
+    torch_device = infer_device()
+
+    def get_model(self):
+        class MyModule(nn.Module):
+            def __init__(self):
+                super().__init__()
+                # choose a large weight so that averages are close to expected values
+                self.linear = nn.Linear(1000, 1000)
+                self.embed = nn.Embedding(1000, 1000)
+                self.conv2d = nn.Conv2d(100, 100, 3)
+
+            def forward(self, x):
+                x_int = (100 * x).int()
+                x_4d = x.flatten().reshape(1, 100, 10, 10)
+                return self.linear(x), self.embed(x_int), self.conv2d(x_4d)
+
+        return MyModule().eval().to(self.torch_device)
+
     def test_use_prompt_tuning_init_text_raises(self):
         with pytest.raises(ValueError, match="When prompt_tuning_init='TEXT', tokenizer_name_or_path can't be None"):
             PromptTuningConfig(prompt_tuning_init="TEXT", prompt_tuning_init_text="prompt tuning init text")