Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
^\.Rprofile$
^\.github$
^\.github/.*
^\.cursor$
^\.cursor/.*
^\.codegraph$
^\.codegraph/.*
^.*\.Rcheck$
Expand All @@ -24,3 +22,4 @@
^\.jules(/.*)?$
^\.trivyignore\.yaml$
^trivy\.yaml$
^\.semgrepignore$
4 changes: 0 additions & 4 deletions .cursor/environment.json

This file was deleted.

50 changes: 0 additions & 50 deletions .cursor/install.sh

This file was deleted.

21 changes: 0 additions & 21 deletions .github/workflows/r.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,33 +35,12 @@ jobs:
r-version: release
use-public-rspm: true

# ponytail: source-build stringfish BEFORE dependency setup — packages built
# from source during install (e.g. SimDesign) load it at build time and die
# on the broken prebuilt binary (oneTBB ABI skew). Drop with the step below.
# repos= must be a real source repo: RSPM serves prebuilt Linux binaries via
# source-style URLs, so type="source" against RSPM does NOT recompile.
# repos= is explicit because R_PROFILE_USER=/dev/null blanks setup-r's repos.
- name: Source-build stringfish (RcppParallel oneTBB ABI skew)
run: >-
Rscript -e 'install.packages(c("RcppParallel", "stringfish"), type = "source",
repos = "https://cloud.r-project.org")'

- name: Set up R package dependencies
uses: r-lib/actions/setup-r-dependencies@d3c5be51b12e724e68f33216ca3c148b66d5f0b6
with:
extra-packages: any::rcmdcheck
needs: check

# ponytail: CRAN/RSPM binary skew — prebuilt binaries (stringfish, qs2, ...) lag
# RcppParallel's oneTBB update (undefined tbb::internal / tbb::task symbols).
# Rebuild everything LinkingTo RcppParallel from source so it links the installed
# copy. Drop once upstream binaries re-sync.
- name: Rebuild RcppParallel-linked packages from source (oneTBB ABI skew)
run: >-
Rscript -e 'r <- "https://cloud.r-project.org"; install.packages("RcppParallel", type = "source", repos = r);
bad <- tools::dependsOnPkgs("RcppParallel", dependencies = "LinkingTo");
if (length(bad)) install.packages(bad, type = "source", repos = r)'

- name: Run R CMD check
uses: r-lib/actions/check-r-package@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590
with:
Expand Down
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
## 2025-02-12 - R 언어에서 반복적인 mirt 모델 생성 시 불필요한 데이터프레임 부분집합 추출 최적화
**Learning:** R에서 데이터프레임의 특정 열을 추출하는 작업(`df[cols]`)은 O(N)의 메모리 복사를 수반합니다. `autoFIPC`에서 `mirt` 모델의 파라미터를 설정하거나 호출하는 과정 중에 `newformXDataK[colnames(newFormModel@Data$data)]` 코드가 반복해서 사용되었고, 심지어 `ncol()`을 위해 단순히 개수를 구할 때도 사용되어 불필요한 메모리 할당과 오버헤드를 초래했습니다.
**Action:** 조건문이나 반복문 내부에서 불필요하게 데이터프레임 부분집합 연산이 반복되지 않도록 외부에서 한 번만 `linkedFormData <- newformXDataK[colnames(newFormModel@Data$data)]`로 캐싱(caching)한 뒤, `ncol(linkedFormData)`와 `data = linkedFormData` 형태로 재사용하여 메모리 복사와 O(N) 오버헤드를 방지해야 합니다.
## 2024-07-23 - R 언어에서 열 이름 추출 시 데이터프레임 부분집합 추출을 피하여 O(N) 메모리 복사 방지
**Learning:** R에서 열 이름을 확인하기 위해 `colnames(df[cols])` 형태로 데이터프레임을 서브셋팅하면, 단순히 이름만 추출하는 경우에도 데이터를 복사하는 과정에서 불필요한 O(N) 메모리 할당과 복사 오버헤드가 발생합니다.
**Action:** 열 이름만 필요할 때는 모델의 이름 벡터를 직접 사용하여 행 수에 비례하는 데이터 프레임 복사를 피해야 합니다. 이름 비교 자체는 열 수에 비례하며, 필수 열을 선택할 때는 직접 서브셋팅하여 누락 열에서 실패하도록 보존해야 합니다.
3 changes: 0 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ parameter calibration and test linking.
- Keep `.github/dependabot.yml` active for GitHub Actions updates.
- Keep `ARCHITECTURE.md` up to date when structure changes.
- Keep README accurate for local verification commands.
- Keep `.cursor/install.sh` revision-agnostic: provision R + CRAN deps only.
Do not `R CMD INSTALL` the current tree in `install` (environment builds
freeze that copy). Use `testthat::test_local()` / `rcmdcheck` on the checkout.

## Editing priorities

Expand Down
9 changes: 2 additions & 7 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ metadata and CI are wired, and which parts are safe to evolve.
- `.github/ISSUE_TEMPLATE/` - structured issue intake templates
- `.github/CODEOWNERS` - code ownership map for reviews
- `.github/dependabot.yml` - Automated Actions dependency updates
- `.cursor/` - Cloud Agent environment (`environment.json` + `install.sh`)
- `docs/coderabbit/review-commands.md` - CodeRabbit command quick reference
- `docs/operations/maintenance-runbook.md` - recurring maintainer operations checklist
- `README.md` - User/developer entrypoint
Expand Down Expand Up @@ -92,10 +91,6 @@ package metadata, and CI workflow definitions in Git.
- Primary check: `R CMD check` via GitHub Actions
- Local check command:
- `Rscript -e 'rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"))'`
- Cloud Agent: `.cursor/environment.json` runs `.cursor/install.sh` to provision
CRAN-release R and r2u binaries (`mirt`, `testthat`, `roxygen2`, `rcmdcheck`).
`install` is snapshot-scoped and must not `R CMD INSTALL` the current tree;
use `testthat::test_local()` / `rcmdcheck` against the checkout.

## 9. Future Considerations / Roadmap

Expand All @@ -108,9 +103,9 @@ package metadata, and CI workflow definitions in Git.
## 10. Project Identification

- Project Name: aFIPC
- Repository URL: `https://github.com/ContextualWisdomLab/aFIPC`
- Repository URL: `https://github.com/seonghobae/aFIPC`
- Primary Contact: Seongho Bae
- Date of Last Update: 2026-08-16
- Date of Last Update: 2026-02-15

## 11. Glossary / Acronyms

Expand Down
2 changes: 0 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ See `ARCHITECTURE.md` for the full map. Essentials:
- `.github/workflows/` — `r.yml` (R CMD check), `code-quality.yml`
(yamllint + markdownlint + actionlint), `security-audit.yml` (gitleaks +
actionlint). All action refs are pinned to full commit SHAs.
- `.cursor/` — Cloud Agent environment. `install.sh` provisions R + CRAN
deps only; do not `R CMD INSTALL` the current tree there.

Runtime is single-process and fileless: in-memory old/new form data ->
`autoFIPC()` -> `mirt` calibration -> optional item parameter drift (IPD)
Expand Down
10 changes: 5 additions & 5 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,8 @@ autoFIPC <-
IPDItemCount <- 0

# IPD target item checking
newFormColNames <- colnames(newformXDataK[colnames(newFormModel@Data$data)])
oldFormColNames <- colnames(oldformYDataK[colnames(oldFormModel@Data$data)])
newFormColNames <- colnames(newFormModel@Data$data)
oldFormColNames <- colnames(oldFormModel@Data$data)

# ⚡ Bolt: Vectorized match() to avoid dynamic array growth overhead inside a for loop
idxNew <- match(newformCommonItemNames, newFormColNames)
Expand Down Expand Up @@ -749,8 +749,8 @@ autoFIPC <-
}
}

newFormColNames <- colnames(newformXDataK[colnames(newFormModel@Data$data)])
oldFormColNames <- colnames(oldformYDataK[colnames(oldFormModel@Data$data)])
newFormColNames <- colnames(newFormModel@Data$data)
oldFormColNames <- colnames(oldFormModel@Data$data)

# ⚡ Bolt: Cache parameter indices to avoid O(N) linear search inside loop
newScaleParmsItemIdxCache <- split(seq_len(nrow(NewScaleParms)), NewScaleParms$item)
Expand Down Expand Up @@ -848,7 +848,7 @@ autoFIPC <-
message('\nestimating Linked Form Eq(X) parameters')

# ⚡ Bolt: Cache subsetted dataframe to avoid repeated O(N) memory copies during mirt model setup
linkedFormData <- newformXDataK[colnames(newFormModel@Data$data)]
linkedFormData <- newformXDataK[, colnames(newFormModel@Data$data), drop = FALSE]

if (forceNormalZeroOne) {
freeMEAN <- F
Expand Down
2 changes: 0 additions & 2 deletions test_dummy.R

This file was deleted.

3 changes: 0 additions & 3 deletions test_validation.R

This file was deleted.

31 changes: 31 additions & 0 deletions tests/testthat/test-bolt-intersect.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
test_that("intersect column name extraction works properly in autoFIPC", {
skip_if_not_installed("mirt")
set.seed(42)

a <- matrix(c(1, 1.2, 0.8, 1.5, 0.9, 1.1, 1.0, 1.3), ncol=1)
d <- matrix(c(1, -1, 0, 0.5, -0.5, 0, 0.2, -0.2), ncol=1)
oldformYData <- mirt::simdata(a, d, 250, itemtype = '2PL')
colnames(oldformYData) <- paste0("Item", 1:8)

a2 <- matrix(c(1, 1.2, 0.8, 1.0, 1.3), ncol=1)
d2 <- matrix(c(1, -1, 0, 0.2, -0.2), ncol=1)
newformXData <- mirt::simdata(a2, d2, 250, itemtype = '2PL')
colnames(newformXData) <- c("Item1", "Item2", "Item3", "NewItem1", "NewItem2")

result <- aFIPC::autoFIPC(
newformXData = newformXData,
oldformYData = oldformYData,
newformCommonItemNames = c('Item1', 'Item2', 'Item3'),
oldformCommonItemNames = c('Item1', 'Item2', 'Item3'),
confirmCommonItems = TRUE,
tryEM = TRUE,
checkIPD = FALSE,
freeMEAN = FALSE,
forceNormalZeroOne = FALSE,
tryFitwholeOldItems = FALSE,
tryFitwholeNewItems = FALSE
)

expect_true(!is.null(result$LinkedModel))
expect_true(methods::is(result$LinkedModel, "SingleGroupClass"))
})
Loading