diff --git a/.github/workflows/type-check.yml b/.github/workflows/ci.yml
similarity index 81%
rename from .github/workflows/type-check.yml
rename to .github/workflows/ci.yml
index 892e723..f960880 100644
--- a/.github/workflows/type-check.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,5 @@
---
-name: Type check
+name: CI
on:
push:
@@ -14,6 +14,19 @@ concurrency:
cancel-in-progress: true
jobs:
+ ruff:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Run Ruff check and format
+ uses: astral-sh/ruff-action@v3
+ with:
+ version-file: pyproject.toml
+ - run: ruff check --no-fix
+ - run: ruff format --diff
+
mypy:
runs-on: ubuntu-latest
steps:
@@ -28,7 +41,6 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
- # Install a specific version of uv.
version: "0.11.14"
enable-cache: true
@@ -52,7 +64,6 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
- # Install a specific version of uv.
version: "0.11.14"
enable-cache: true
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index c0aff0c..0000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-name: Lint
-
-on:
- push:
- branches:
- - main
- pull_request:
- branches:
- - main
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- ruff:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Run Ruff check and format
- uses: astral-sh/ruff-action@v3
- with:
- version-file: pyproject.toml
- args: --version
- - run: ruff check --no-fix
- - run: ruff format --diff
diff --git a/.gitignore b/.gitignore
index c51babc..a26079b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,13 @@
.vscode
data
+final-artefacts
logs
/models/*
notebooks/*.ipynb
!notebooks/kaggle_grid_search.ipynb
!notebooks/kaggle_sasrec_training.ipynb
reports
+tests
final-artefacts
# Created by https://www.toptal.com/developers/gitignore/api/python
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000..372480c
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,22 @@
+cff-version: 1.2.0
+message: "If you use this software, please cite it as below."
+type: software
+authors:
+ - family-names: Głowacz
+ given-names: Krzysztof
+ orcid: https://orcid.org/0009-0009-0914-7395
+title: "IMPACT: Intention-Modulated Personalized ACT-R"
+abstract: "A hybrid sequential/cognitive-memory music recommender system extending SASRec
+ with a personalised popularity signal derived from the ACT-R cognitive architecture,
+ weighted by listen intentionality."
+repository-code: "https://github.com/Krisenberg/impact"
+url: "https://github.com/Krisenberg/impact"
+date-released: 2026-06-15
+license: MIT
+keywords:
+ - recommender-systems
+ - sequential-recommendation
+ - music-recommendation
+ - sasrec
+ - act-r
+ - cognitive-architecture
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1e8a21a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Krzysztof Głowacz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 35f726d..85800fd 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,97 @@
-# IMPACT (Intention-Modulated Personalized ACT-R)
+# IMPACT
-## Development
+**I**ntention-**M**odulated **P**ersonalized **ACT**-R — a hybrid sequential/cognitive-memory recommender system for music that beats SASRec (established baseline) by 114% NDCG@10 on the 4.8B-interaction Yambda-5B dataset. The full pipeline — dataset download, model training and system evaluation — runs within 16GB of RAM with no GPU and no OOM, though SASRec was trained on a GPU for speed.
-### Pre-commit Hooks
+Built for master's thesis at Wrocław University of Science and Technology.
-[prek](https://prek.j178.dev/) (which is a "reimagined version of [pre-commit](https://pre-commit.com/), written in Rust") runs automated checks before every `git commit`.
+[](https://github.com/Krisenberg/impact/actions/workflows/ci.yml)
+
+
+
+[](https://github.com/astral-sh/uv)
+[](https://github.com/astral-sh/ruff)
+[](https://docs.basedpyright.com)
-**Setup:**
+Sequential recommender systems, such as SASRec [[1]](#1), model what a user wants *right now*, within a short window of recent sessions. However, music listening is dominated by repetition — people return to their favourite tracks for months or even years — and this long-term signal is completely outside the scope of a short attention window. **IMPACT** adds it back in by using a personalised popularity signal derived from **ACT-R** [[2]](#2), a cognitive architecture from 1993 that models how human declarative memory strengthens and weakens with use. This signal is weighted by whether each listen was actively chosen by the user or just passively served by the streaming platform. The two models operate independently and are combined at inference time, so the final "blend" can be adjusted/retuned without retraining SASRec (ACT-R is purely deterministic either way).
-```bash
-uv tool install prek # install prek (recommended way - using `uv tool` command)
-prek install # install hooks into .git\hooks\pre-commit
-```
+Experiments showed that ACT-R achieves the highest ranking accuracy (NDCG@10), but can only recommend tracks already present in the user's listening history — its global exploration score is exactly 0.000 by definition. SASRec can recommend novel, previously unheard tracks, but at substantially lower ranking accuracy. **IMPACT** is "the best of both worlds", and on the full-scale **Yambda-5B** dataset (4.8 billion interactions) [[3]](#3) **improves NDCG@10 by +114%** relative to SASRec, while still being able to recommend tracks that the user has never heard before.
-Now every `git commit` will invoke `prek run` for the files **included in the commit**. If you ever want to undo this, run `prek uninstall`.
+## Architecture
-If you want to manually execute all configured hooks on the files in your current git staging area, run:
+The two components — SASRec and ACT-R — are fully decoupled and independent of each other. Fusion happens only at inference time, via a hyperparameter alpha.
-```bash
-prek run
-```
+
+
+
-To target all files (the entire repository), add `--all-files` flag to the command:
+## Results
-```bash
-prek run --all-files
-```
+Three configurations of IMPACT were evaluated on the held-out Yambda-5B test set: pure ACT-R, the optimal (empirically found) hybrid and pure SASRec.
-To bypass all hooks, add `--no-verify` flag to the `git commit` command:
+| Metric | ACT-R (alpha=0) | **IMPACT (alpha=0.4)** | SASRec (alpha=1) |
+|---|---|---|---|
+| NDCG@10 | 0.0894 | **0.0650** | 0.0304 |
+| Recall@10 | 0.0805 | **0.0624** | 0.0258 |
-```bash
-git commit --no-verify -m "your message"
-```
+The results presented above, which use only overall metrics, tell a misleading story: ACT-R performs better than the hybrid with SASRec on both NDCG@10 and Recall@10, which might suggest that the SASRec component is unnecessary. To understand why ACT-R is superior and whether that superiority is desirable, the NDCG@10 metric is analysed along two dimensions — whether the recommended track is one the user already knows (*repetition*) or genuinely new to them (*exploration*), and whether the "knownness" is judged against the user's recent session window (*local*) or their entire listening history (global):
+
+| NDCG@10, decomposed | ACT-R (alpha=0) | **IMPACT (alpha=0.4)** | SASRec (alpha=1) |
+|---|---|---|---|
+| Repetition, local | 0.1445 | **0.1024** | 0.0444 |
+| Exploration, local | 0.0138 | **0.0098** | 0.0070 |
+| Repetition, global | 0.1004 | **0.0720** | 0.0328 |
+| Exploration, global | 0.0000 | **0.0022** | 0.0035 |
+
+The NDCG@10 metric decomposition reveals the trade-off introduced earlier. ACT-R's accuracy advantage is entirely driven by repetition — it performs well because music consumption is repetitive by nature, and ACT-R is specifically designed to model such behaviour. However, the downside is that its global exploration score is 0.000, meaning it cannot recommend a track outside the user's listening history (which leads to a filtering bubble). SASRec is at the other extreme — it can recommend genuinely new content, but with only a fraction of ACT-R's accuracy. **IMPACT bridges this gap** by retaining most of ACT-R's repetition-driven accuracy while maintaining a non-zero exploration ability — the outcome the hybrid was designed to produce.
+
+**Selecting alpha.** The three configurations evaluated above use $\alpha \in {0, 0.4, 1}$, but alpha is a continuous hyperparameter. To select the optimal value without overfitting to the held-out test set, all five NDCG@10 variants (overall + four decomposed) were swept across $\alpha \in [0, 1]$ in steps of 0.05 on the validation set of the smaller Yambda-50M variant (1% of the full dataset), used as a computational proxy. The found $\alpha_opt$ = 0.4 was then applied to the held-out Yambda-5B test set without further tuning.
+
+
+
+
+
+The plot makes the tradeoff clear: overall accuracy and repetition-based metrics decline monotonically as alpha increases, while global exploration is exactly 0.000 for small alpha values and only becomes non-zero once SASRec contributes enough weight to the fusion. $\alpha = 0.4$ is the point where the system recovers most of the exploration ability without sacrificing an unreasonable amount of the accuracy advantage that ACT-R provides. A pure ACT-R configuration ($\alpha = 0$) would score highest on every accuracy metric but would never recommend a track outside the user's history — it would always lead to a filter bubble by definition, regardless of the user's actual openness to new content.
+
+**Statistical significance.** To test whether the improvement is statistically significant, a Wilcoxon signed-rank test for paired data was applied to per-session NDCG@10 differences $d_i$ = NDCG@10(IMPACT) − NDCG@10(SASRec) across the full test set, under the following hypotheses:
+
+- $H_0$: The median of $d_i$ is zero — IMPACT does not outperform SASRec.
+- $H_1$: The median of $d_i$ is positive — IMPACT systematically achieves a higher NDCG@10 than SASRec.
+
+The test rejected $H_0$ in favour of $H_1$ with a p-value $<< 0.001$ and an effect size r = 0.771 (Cohen's $d$ $\approx$ 2.42).
+
+Details that are worth stating explicitly:
+- **Effect size scope:** an effect size of r = 0.771 describes the strength and consistency of the difference among the 5,960 pairs (26.8% of the test set) in which the two systems actually differed. For the remaining 73.2%, both models returned identical scores — predominantly NDCG@10 = 0 for both, which is typical in sparse recommendation scenarios. The effect is large where it exists, but not uniformly large across the full test set.
+- **Direction:** of the 5,960 non-zero pairs, IMPACT outperformed SASRec in 5,182 cases (86.9%) and underperformed in 778 (13.1%) — a ratio of approximately 6.7:1 in favour of IMPACT, which is consistent with the one-sided hypothesis $H_1$. This could be understood like the following: **where introducing the personalised popularity signal changes the prediction at all, that change is nearly seven times more likely to help than to hurt.**
+
+## Citation
+
+The accompanying master's thesis is undergoing formal review and defence at Wrocław University of Science and Technology, and cannot be shared publicly yet.
+
+This repository serves as the primary citable artifact. It contains the complete implementation of IMPACT, together with the experimental pipelines, diagrams, and results required to reproduce the findings reported in the thesis.
+
+If you use this software, please cite it using the entry provided in [`CITATION.cff`](CITATION.cff).
+
+## References
+
+[1]
+Wang-Cheng Kang and Julian McAuley, 2018.
+Self-Attentive Sequential Recommendation.
+DOI: [10.48550/arXiv.1808.09781](https://doi.org/10.48550/arXiv.1808.09781)
+
+[2]
+John R. Anderson et al., 2004.
+Psychological Review 111.4.
+DOI: [10.1037/0033-295X.111.4.1036](https://doi.org/10.1037/0033-295X.111.4.1036)
+
+[3]
+Alexander Ploshkin et al., 2025.
+Yambda-5B — A Large-Scale Multi-Modal Dataset for Ranking and Retrieval.
+DOI: [10.1145/3705328.3748163](https://doi.org/10.1145/3705328.3748163)
+
+## License
+
+MIT — see [`LICENSE`](LICENSE).
diff --git a/assets/alpha_sweep_plot.svg b/assets/alpha_sweep_plot.svg
new file mode 100644
index 0000000..9ef4322
--- /dev/null
+++ b/assets/alpha_sweep_plot.svg
@@ -0,0 +1 @@
+
diff --git a/assets/impact_architecture.svg b/assets/impact_architecture.svg
new file mode 100644
index 0000000..f51cbdd
--- /dev/null
+++ b/assets/impact_architecture.svg
@@ -0,0 +1,992 @@
+
+