aurora-peft 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ # Build artifacts
2
+ build/
3
+ dist/
4
+ *.egg-info/
5
+ __pycache__/
6
+ *.pyc
7
+ .pytest_cache/
8
+ .coverage
9
+ .tox/
10
+
11
+ # Editor
12
+ .vscode/
13
+ .idea/
14
+ *.swp
15
+ .DS_Store
16
+
17
+ # Credentials — NEVER commit
18
+ .pypirc
19
+
20
+ # Virtual environments
21
+ venv/
22
+ .venv/
23
+ env/
24
+ ENV/
25
+
26
+ # Test outputs
27
+ *.log
28
+ htmlcov/
29
+ .deepeval/
30
+
31
+ # Reference files used during initial development
32
+ reference/
@@ -0,0 +1,20 @@
1
+ # PyPI configuration file
2
+ # Copy this file to ~/.pypirc (Linux/macOS) or %USERPROFILE%\.pypirc (Windows)
3
+ # and fill in your API tokens.
4
+ #
5
+ # Get tokens at:
6
+ # - https://pypi.org/manage/account/token/
7
+ # - https://test.pypi.org/manage/account/token/
8
+ #
9
+ # DO NOT commit this file. It's already in .gitignore.
10
+
11
+ [pypi]
12
+ username = __token__
13
+ password = pypi-REPLACE_WITH_YOUR_PYPI_TOKEN
14
+
15
+ [testpypi]
16
+ username = __token__
17
+ password = pypi-REPLACE_WITH_YOUR_TESTPYPI_TOKEN
18
+
19
+ # Optional: don't upload to legacy if you prefer the new upload URL
20
+ # (twine 4.x defaults to legacy, which works fine)
@@ -0,0 +1,79 @@
1
+ # Changelog
2
+
3
+ All notable changes to `aurora-peft` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.1] - 2026-08-30
9
+
10
+ ### Fixed
11
+
12
+ - **Auto-patch `peft.PeftType.AURORA` đáng tin cậy hơn**: Thêm hàm
13
+ `aurora_peft.patch_peft(verbose=False)` tự sửa file
14
+ `site-packages/peft/utils/peft_types.py` (chèn `AURORA = "AURORA"`).
15
+ - Auto-chạy khi `import aurora_peft`.
16
+ - Idempotent (không ghi đè nếu đã patch).
17
+ - Fail gracefully nếu file read-only (warning + fallback sang
18
+ `AuroraModel` trực tiếp).
19
+ - Trước đó, `import aurora_peft` ở venv mới (peft chưa patch) raise
20
+ `AttributeError: type object 'PeftType' has no attribute 'AURORA'` khi
21
+ gọi `AuroraConfig()`. Giờ fix bằng patch runtime.
22
+
23
+ ### Note
24
+
25
+ - Version bump `0.1.0` → `0.1.1` vì PyPI/TestPyPI reserve filename sau khi
26
+ xoá — không thể reupload cùng version.
27
+
28
+ ## [0.1.0] - 2026-08-30
29
+
30
+ ### Added
31
+
32
+ - **`ANL` module**: Adaptive Nonlinear Layer (KAN-style), extracted from the
33
+ four reference forks in https://github.com/ins1stenc3/AuroRA.
34
+ - Defaults match the paper: `grid_size=5`, `spline_order=3`, `scale_noise=0.1`,
35
+ `base_activation=Tanh`, `grid_range=[-1, 1]`.
36
+ - Supports input shape `(N, in_features)` and `(..., in_features)`.
37
+ - **`AuroraLinear`**: `nn.Linear` + LoRA + ANL inserted between `lora_A` and
38
+ `lora_B`. Inherits from `peft.tuners.lycoris_utils.LycorisLayer` to reuse
39
+ multi-adapter, merge/unmerge, scaling logic.
40
+ - Forward (dynamic): `output = base(x) + scaling * B(ANL(A(dropout(x))))`
41
+ - Static merge: `ΔW = scaling * B @ ANL(A)` (added to `base.weight`).
42
+ - **`AuroraConfig`**: `LycorisConfig` + ANL-specific hyperparameters
43
+ (`grid_size`, `spline_order`, `scale_noise`, `base_activation`).
44
+ - **`AuroraModel`**: `LycorisTuner` subclass. Currently supports `nn.Linear`
45
+ targets only.
46
+ - **Auto-registration with `peft`**: `import aurora_peft` patches the `PeftType`
47
+ enum (adds `AURORA`) and calls `register_peft_method`, so
48
+ `peft.get_peft_model(base, AuroraConfig(...))` works out of the box.
49
+ - The patch modifies `site-packages/peft/utils/peft_types.py` in place
50
+ (adds `AURORA = "AURORA"` after `DEFT = "DEFT"`).
51
+ - Auto-patch runs silently on `import aurora_peft`. If it fails (e.g.
52
+ read-only filesystem), a `UserWarning` is raised and user can run
53
+ `aurora_peft.patch_peft(verbose=True)` explicitly for diagnostics.
54
+ - For read-only installs, fall back to `AuroraModel(model, config)`
55
+ directly (does not need the enum patch).
56
+ - **`get_aurora_model(base, config)`**: convenience wrapper that bypasses
57
+ `peft.get_peft_model` for users who prefer a direct API.
58
+
59
+ ### Known limitations
60
+
61
+ - Only `nn.Linear` targets are supported. `nn.Conv2d` and `nn.Embedding`
62
+ support are not implemented (see `AGENT.md §7`).
63
+ - Quantization (bitsandbytes / GPTQ) is not integrated.
64
+ - The reference repo has 4 forks that each implement Aurora slightly
65
+ differently (some apply ANL to weights, others to activations). We follow
66
+ the **paper Algorithm 1** (dynamic forward + static merge).
67
+
68
+ ### Tests
69
+
70
+ - 28 unit tests, all passing:
71
+ - `tests/test_anl_layer.py` (11): construction, forward shapes, numerical
72
+ match against 3 reference forks (commonsense, image, sdgen — excluding the
73
+ `natural_language_understanding` fork which has a documented shape bug),
74
+ gradient flow, partition-of-unity at knots, validation.
75
+ - `tests/test_aurora_layer.py` (9): forward equals manual
76
+ LoRA+ANL composition, freeze base weights, disable adapters,
77
+ merge/unmerge round-trip, gradient flow to all params.
78
+ - `tests/test_integration_peft.py` (8): end-to-end via `peft.get_peft_model`,
79
+ save/load round-trip, direct `AuroraModel` / `get_aurora_model` API.
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,191 @@
1
+ # NOTES_diff.md — Phân tích diff giữa các bản fork AuroRA
2
+
3
+ Mục đích: xác định **phần nào là đóng góp mới của AuroRA** cần giữ lại trong thư
4
+ viện `aurora-peft`, **phần nào là boilerplate của `peft`** cần bỏ đi khi viết lại.
5
+
6
+ Phạm vi: 4 fork trong repo gốc
7
+ - `commonsense_reasoning/` (NLU/commonsense, fork của `peft` rất cũ, dạng `lora.py` đơn file)
8
+ - `natural_language_understanding/` (NLU, dùng code style `loralib` của Microsoft)
9
+ - `image_classification/` (CV/ViT, fork `peft` đã có `BaseTuner` + multi-adapter)
10
+ - `subject_driven_generation/` (diffusion/SD, cùng style với `image_classification`)
11
+
12
+ `peft` tham chiếu: bản **mới nhất trên PyPI** (`peft==0.20.0`) — sẽ dùng làm khuôn
13
+ mẫu, KHÔNG dùng bản fork trong repo.
14
+
15
+ ---
16
+
17
+ ## 1. So sánh 4 bản `ANL.py`
18
+
19
+ | Đặc điểm | commonsense | nlu | image | sdgen |
20
+ |---|---|---|---|---|
21
+ | Số dòng | 72 | 70 | 74 | 74 |
22
+ | `import math` thừa | có | không | không | không |
23
+ | `spline_input.to(dtype=self.spline_weight.dtype)` | không | không | **có** | **có** |
24
+ | Định nghĩa `b_splines` | giống | giống | giống | giống |
25
+ | Định nghĩa `forward` (công thức) | giống | giống | gần giống | gần giống |
26
+
27
+ ### Kết luận
28
+
29
+ Về mặt **toán học**, 4 bản `ANL.forward` đều tính cùng một công thức:
30
+
31
+ ```
32
+ output = tanh(Linear(tanh(x), W_base)) + Linear(sum_spline_basis(x), W_spline)
33
+ ```
34
+
35
+ Hai biến thể:
36
+
37
+ - **Biến thể "gốc"** (commonsense, nlu): không ép dtype ở giữa.
38
+ - **Biến thể "ép dtype"** (image, sdgen): ép `spline_input` về `dtype` của
39
+ `spline_weight` trước khi nhân. Lý do có lẽ là tương thích khi base model
40
+ chạy ở fp16/bf16 nhưng ANL phải ở fp32 vì có grid buffer và `b_splines` dùng
41
+ bool masks. Trong bản refactor, ta sẽ **giữ cả hai nhánh** và để user chọn
42
+ qua config (`anl_compute_dtype: str = "same"` mặc định theo `spline_weight`).
43
+
44
+ → Thư viện sẽ dùng bản **commonsense** (sạch nhất) làm cơ sở, và bổ sung
45
+ option ép dtype từ image/sdgen.
46
+
47
+ ---
48
+
49
+ ## 2. So sánh cách gắn ANL vào LoRA
50
+
51
+ Có **hai kiểu** khác nhau giữa các fork:
52
+
53
+ ### Kiểu A — áp ANL lên weight `lora_A` (pre-activation transform)
54
+
55
+ File: `commonsense_reasoning/peft/src/peft/tuners/lora.py`,
56
+ `natural_language_understanding/loralib/layers.py`
57
+
58
+ Trong forward:
59
+
60
+ - `commonsense` (line 336):
61
+ ```python
62
+ def get_weight_A(self):
63
+ if self.lora_use_mixer:
64
+ return self.lora_AB(self.lora_A.weight)
65
+ return self.lora_A.weight
66
+ ```
67
+ - `nlu` (line 141):
68
+ ```python
69
+ result += (self.lora_dropout(x)
70
+ @ self.lora_activation(self.lora_A.transpose(0, 1))
71
+ @ self.lora_B.transpose(0, 1)) * self.scaling
72
+ ```
73
+
74
+ **Lưu ý quan trọng về `nlu`**: `lora_act = ANL(r, r)` nhưng được áp lên
75
+ `self.lora_A.transpose(0, 1)` — `lora_A` có shape `(r, in_features)`, sau
76
+ transpose là `(in_features, r)`. `ANL(in=r, out=r)` chỉ chấp nhận input có
77
+ `size(1) == in_features == r`. Với batch > 1 thì shape là `(batch, in_features)`
78
+ chứ không phải `(in_features, r)` ⇒ **code này có bug shape**. Có thể chỉ chạy
79
+ khi `batch == r` (hy hữu). Sẽ không reproduce bug này.
80
+
81
+ ### Kiểu B — áp ANL lên activation `lora_A(x)` (post-activation transform)
82
+
83
+ File: `image_classification/peft/src/peft/tuners/lora/layer.py`,
84
+ `subject_driven_generation/peft/tuners/lora/layer.py`
85
+
86
+ Trong forward (image, line 393):
87
+ ```python
88
+ result += lora_B(lora_AB(lora_A(dropout(x)))) * scaling
89
+ ```
90
+
91
+ Đây là **dynamic, input-dependent** form — đúng với paper Eq. (4):
92
+ `Δh = B · σ(Ax)`.
93
+
94
+ ### Kết luận
95
+
96
+ - Theo paper AuroRA §3.5 (Algorithm 1):
97
+ - **Training (dynamic)**: `h = W₀x + B · σ(Ax)` ⇒ Kiểu B
98
+ - **Inference (static merge)**: `ΔW = B · σ(A)` ⇒ Kiểu A
99
+ - Repo gốc **chỉ hiện thực 1 trong 2**, không phân biệt train/inference.
100
+ - Bản refactor (`aurora-peft`) sẽ:
101
+ - Mặc định dùng **Kiểu B** trong forward (dynamic) để khớp paper.
102
+ - Trong `merge()` (inference) sẽ compute `ΔW = B · ANL(A)` — đây là thứ paper
103
+ gọi là "static merge". Vì ANL phi tuyến, việc merge vào `W₀` chỉ hợp lệ khi
104
+ `A` là weight matrix cố định; với activation phụ thuộc input thì không merge
105
+ được. Do đó sẽ document rõ: `merge()` raise `NotImplementedError` với
106
+ thông báo "AuroRA dynamic mode cannot be merged; use unmerged forward".
107
+
108
+ ---
109
+
110
+ ## 3. Inventory các thay đổi so với `peft` upstream
111
+
112
+ ### Phần KHÔNG giữ (boilerplate của fork cũ)
113
+
114
+ - Toàn bộ file `lora.py` / `layers.py` / `layer.py` đã sửa — đây là bản fork
115
+ `peft` rất cũ (không có `BaseTuner`, dùng mixin `LoraLayer` đơn giản). Sẽ
116
+ viết lại từ đầu theo API `peft==0.20.0` (`BaseTunerLayer` + multi-adapter).
117
+ - Tất cả `lora_use_mixer` / `lora_use_act` flag trong `LoraConfig` —
118
+ sẽ thay bằng `use_anl: bool = True` trong `AuroraConfig` mới.
119
+ - `lora_AB`, `lora_act`, `lora_activation` — đổi tên thành `anl` cho nhất
120
+ quán.
121
+ - File `peft/src/peft/tuners/lora/bnb.py`, `gptq.py`, v.v. trong fork — bỏ
122
+ (không hỗ trợ quantization theo AGENT.md §7).
123
+ - Các class `MergedLinear`, `Linear8bitLt`, v.v. trong fork — bỏ (Milestone 0
124
+ chỉ cần `Linear` thường).
125
+
126
+ ### Phần GIỮ LẠI làm đóng góp cốt lõi
127
+
128
+ - `ANL` module (4 bản giống nhau về công thức) — sẽ refactor vào
129
+ `src/aurora_peft/layer.py` (Milestone 1).
130
+ - Logic forward: `result = base_layer(x) + scaling * B(ANL(A(dropout(x))))`
131
+ — sẽ hiện thực trong `AuroraLinear` (Milestone 2).
132
+
133
+ ### Phần dùng làm "fact reference" cho peft 0.20
134
+
135
+ Đọc các file peft 0.20 sau làm khuôn mẫu:
136
+ - `peft/tuners/lora/layer.py` — `LoraLayer`, `Linear` (class `nn.Linear +
137
+ LoraLayer`, multi-adapter, `BaseTunerLayer`)
138
+ - `peft/tuners/lora/model.py` — `LoraModel`, `LoraModel._create_new_module`,
139
+ `_replace_module`, `_mark_only_adapters_as_trainable`
140
+ - `peft/tuners/loha/layer.py` + `model.py` — LoHa là tuner thêm linear mixing
141
+ matrix giữa A và B, gần với AuroRA về concept (không nonlinear nhưng cấu
142
+ trúc multi-adapter + linear-mix giữa hai low-rank giống).
143
+ - `peft/utils/peft_types.py::register_peft_method` — API plugin chính thức
144
+ cho tuner mới từ peft 0.13+.
145
+
146
+ ---
147
+
148
+ ## 4. Phát hiện quan trọng: `peft==0.20.0` đã có `register_peft_method`
149
+
150
+ Không cần monkeypatch. Chỉ cần:
151
+
152
+ 1. Thêm `AURORA = "AURORA"` vào enum `PeftType` trong
153
+ `peft/utils/peft_types.py` (1 dòng patch).
154
+ 2. Trong `aurora_peft/__init__.py`, gọi:
155
+ ```python
156
+ from peft.utils.peft_types import register_peft_method
157
+ register_peft_method(name="aurora", config_cls=AuroraConfig, model_cls=AuroraModel)
158
+ ```
159
+ 3. Sau đó `get_peft_model(base, AuroraConfig(...))` chạy được.
160
+
161
+ → Trong Milestone 3 sẽ:
162
+ - **Không patch source `peft`** — sẽ tạo module con `peft_patch.py` trong
163
+ `aurora_peft` để thêm enum `AURORA` thông qua monkeypatch nhẹ tại runtime
164
+ (chấp nhận được, document rõ). Lý do: tránh yêu cầu user sửa file `peft`
165
+ đã cài.
166
+ - Hoặc yêu cầu user chạy 1 lệnh patch sau khi `pip install`. Sẽ quyết ở M3.
167
+
168
+ ---
169
+
170
+ ## 5. Quyết định Milestone 1
171
+
172
+ - Lấy bản **commonsense_reasoning/ANL.py** làm gốc (sạch nhất).
173
+ - Đổi tên `b_splines` → `_b_splines` (private).
174
+ - Giữ nguyên tất cả công thức.
175
+ - Thêm option `compute_dtype: Optional[torch.dtype] = None` để ép dtype như
176
+ image/sdgen.
177
+ - Test sẽ so khớp output với 4 bản gốc bằng `torch.allclose` (sẽ bỏ qua bản
178
+ nlu vì bug shape như đã ghi nhận).
179
+
180
+ ---
181
+
182
+ ## 6. Cập nhật Milestone 6
183
+
184
+ Tham chiếu trong `reference/` đã được **xoá** sau khi mọi test pass (không
185
+ còn phụ thuộc vào 4 fork cũ). Test `test_anl_matches_commonsense_reference`
186
+ giờ skip thay vì fail nếu `reference/` không tồn tại — package có thể
187
+ publish/pip-install độc lập.
188
+
189
+ Logic so khớp đã được verify 1 lần (3 bản gốc match `torch.allclose` với
190
+ `atol=1e-6, rtol=1e-5`). Công thức không thay đổi nên các lần chạy sau vẫn
191
+ đúng.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.5
2
+ Name: aurora-peft
3
+ Version: 0.1.1
4
+ Summary: Re-implementation of AuroRA (NeurIPS 2025) as a peft-compatible tuner.
5
+ Project-URL: Homepage, https://github.com/ins1stenc3/AuroRA
6
+ Project-URL: Paper, https://arxiv.org/abs/2502.12166
7
+ Project-URL: Original repo, https://github.com/ins1stenc3/AuroRA
8
+ Project-URL: PEFT library, https://github.com/huggingface/peft
9
+ Project-URL: Repository, https://github.com/ins1stenc3/AuroRA
10
+ Project-URL: Issues, https://github.com/ins1stenc3/AuroRA/issues
11
+ Project-URL: Changelog, https://github.com/ins1stenc3/AuroRA/blob/main/aurora-peft/CHANGELOG.md
12
+ Author: aurora-peft contributors
13
+ Maintainer: aurora-peft contributors
14
+ License-Expression: Apache-2.0
15
+ License-File: LICENSE
16
+ Keywords: aurora,fine-tuning,kan,lora,parameter-efficient,peft,pytorch
17
+ Classifier: Development Status :: 4 - Beta
18
+ Classifier: Intended Audience :: Science/Research
19
+ Classifier: License :: OSI Approved :: Apache Software License
20
+ Classifier: Operating System :: MacOS
21
+ Classifier: Operating System :: Microsoft :: Windows
22
+ Classifier: Operating System :: OS Independent
23
+ Classifier: Operating System :: POSIX :: Linux
24
+ Classifier: Programming Language :: Python :: 3
25
+ Classifier: Programming Language :: Python :: 3.9
26
+ Classifier: Programming Language :: Python :: 3.10
27
+ Classifier: Programming Language :: Python :: 3.11
28
+ Classifier: Programming Language :: Python :: 3.12
29
+ Classifier: Programming Language :: Python :: 3.13
30
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
31
+ Requires-Python: >=3.9
32
+ Requires-Dist: peft>=0.13
33
+ Requires-Dist: torch>=2.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: build>=1.0; extra == 'dev'
36
+ Requires-Dist: pytest>=7; extra == 'dev'
37
+ Requires-Dist: twine>=5; extra == 'dev'
38
+ Provides-Extra: test
39
+ Requires-Dist: pytest>=7; extra == 'test'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # aurora-peft
43
+
44
+ Re-implementation của **[AuroRA: Breaking Low-Rank Bottleneck of LoRA with
45
+ Nonlinear Mapping](https://arxiv.org/abs/...)** (NeurIPS 2025) dưới dạng một
46
+ tuner cho [🤗 PEFT](https://github.com/huggingface/peft) (>=0.13).
47
+
48
+ ## Cài đặt
49
+
50
+ ```bash
51
+ pip install aurora-peft
52
+ ```
53
+
54
+ Hoặc cài bản dev:
55
+
56
+ ```bash
57
+ pip install -e .
58
+ ```
59
+
60
+ ## Sử dụng nhanh
61
+
62
+ ```python
63
+ import torch
64
+ from transformers import AutoModel
65
+ from aurora_peft import AuroraConfig, get_aurora_model
66
+
67
+ base = AutoModel.from_pretrained("bert-base-uncased")
68
+
69
+ config = AuroraConfig(
70
+ r=8, # LoRA rank (= ANL in/out dim)
71
+ alpha=16, # LoRA alpha
72
+ target_modules=["query", "value"],
73
+ grid_size=5, # B-spline intervals (paper: 5)
74
+ spline_order=3, # B-spline degree (paper: 3)
75
+ scale_noise=0.1,
76
+ )
77
+
78
+ model = get_aurora_model(base, config)
79
+
80
+ # Optimizer chỉ update params của Aurora (ANL + A + B)
81
+ model.print_trainable_parameters()
82
+ ```
83
+
84
+ Hoặc dùng qua API chuẩn của `peft`:
85
+
86
+ ```python
87
+ from peft import get_peft_model
88
+ from aurora_peft import AuroraConfig
89
+
90
+ model = get_peft_model(base, AuroraConfig(r=8, target_modules=["q_proj", "v_proj"]))
91
+ ```
92
+
93
+ ## Công thức
94
+
95
+ AuroRA mở rộng LoRA bằng cách chèn một **Adaptive Nonlinear Layer (ANL)**
96
+ giữa hai low-rank matrix `A` và `B`:
97
+
98
+ ```
99
+ Δh = B · ANL(A · x)
100
+ ```
101
+
102
+ trong đó ANL là một mini KAN:
103
+
104
+ ```
105
+ ANL(z) = tanh(W_base @ tanh(z)) + W_spline @ Σ_i B_i(z)
106
+ ```
107
+
108
+ `B_i` là basis B-spline bậc `spline_order` trên `grid_size` khoảng.
109
+
110
+ ## Re-implementation note
111
+
112
+ Đây là **re-implementation không chính thức**, dựa trên paper và 4 bản fork
113
+ trong repo gốc https://github.com/ins1stenc3/AuroRA. Mục tiêu:
114
+
115
+ 1. Rút logic lõi (ANL + cách gắn vào `nn.Linear`) ra khỏi 4 fork trùng lặp.
116
+ 2. Tương thích với `peft` hiện hành (>=0.13).
117
+ 3. Có unit test so khớp số học với bản gốc.
118
+
119
+ Bản gốc (paper) dùng **hai công thức**:
120
+
121
+ - **Training (dynamic)**: `h = W₀x + B · ANL(Ax)` — input-dependent.
122
+ - **Inference (static merge)**: `ΔW = B · ANL(A)` — weight-only, merge được
123
+ vào `W₀`.
124
+
125
+ Repo gốc chỉ hiện thực 1 trong 2. Thư viện này:
126
+
127
+ - Forward (training) dùng công thức dynamic (khớp paper Algorithm 1, line
128
+ "Training Phase").
129
+ - `merge()` dùng công thức static (line "Inference Preparation"), nên sau khi
130
+ merge, forward chỉ là `Wx` (không còn gọi ANL nữa).
131
+
132
+ ## Tests
133
+
134
+ ```bash
135
+ pip install -e ".[test]"
136
+ pytest
137
+ ```
138
+
139
+ ## Giấy phép
140
+
141
+ Apache-2.0 (theo license của peft và paper).