From 021e8fdc9de744db5cc549a1e8e80948a1919e01 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 21:13:43 +0000 Subject: [PATCH 1/3] Fix twin matrix size mismatch between addtwins and merging methods Two issues caused the sparse matrices from the two mz_method paths to differ structurally: 1. The merging method iterated over mz_pairs (row indices) but treated the values as IDs. Changed to iterate over mz_id_pairs which holds the actual pedigree IDs. Currently masked because test data has sequential IDs matching row positions, but would break with non-sequential IDs. 2. The different computational paths (column-merge-before-tcrossprod vs pedigree-modify-then-post-copy) leave different explicit zeros in the sparse matrix. Added Matrix::drop0() before returning to normalize the sparse representation so both methods produce structurally identical results. https://claude.ai/code/session_01PXbgV7bdA9sg7St6xBbg3T --- R/buildComponent.R | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/R/buildComponent.R b/R/buildComponent.R index bad3b3f6..e0392390 100644 --- a/R/buildComponent.R +++ b/R/buildComponent.R @@ -144,7 +144,7 @@ ped2com <- function(ped, component, c(ped$ID[pair[1]], ped$ID[pair[2]]) }) - for (id_pair in mz_pairs) { + for (id_pair in mz_id_pairs) { twin1_id <- id_pair[1] twin2_id <- id_pair[2] twin2_row <- which(ped$ID == twin2_id) @@ -383,7 +383,11 @@ ped2com <- function(ped, component, # Assign 1 to all nonzero elements for mitochondrial component } - if (config$sparse == FALSE) { + # Remove explicit zeros so that both mz_method paths produce + # structurally identical sparse matrices + if (config$sparse == TRUE) { + r <- Matrix::drop0(r) + } else { r <- as.matrix(r) } # flattens diagonal if you don't want to deal with inbreeding From ed2f5999f8edd101b2488f7b521b5e1912540677 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 21:44:18 +0000 Subject: [PATCH 2/3] Fix sparse matrix class mismatch between MZ twin methods The merging method's post-copy (r[idx2,] <- r[idx1,]) causes Matrix to coerce from dsCMatrix (symmetric, one triangle) to dgCMatrix (general, both triangles), doubling @i/@x slot lengths. Add forceSymmetric() after the post-copy to restore symmetric storage. Also guard the merging pedigree modification with a component check (additive only) to match the restoration block, and move the verbose message inside the merging if-block so it only prints when merging actually occurs. https://claude.ai/code/session_01PXbgV7bdA9sg7St6xBbg3T --- R/buildComponent.R | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/R/buildComponent.R b/R/buildComponent.R index 95cf33e5..75f79f64 100644 --- a/R/buildComponent.R +++ b/R/buildComponent.R @@ -144,7 +144,8 @@ ped2com <- function(ped, component, return(readRDS(checkpoint_files$final_matrix)) } - if (mz_method %in% c("merging") && mz_twins == TRUE && !is.null(mz_pairs) && length(mz_pairs) > 0) { + if (mz_method %in% c("merging") && mz_twins == TRUE && !is.null(mz_pairs) && length(mz_pairs) > 0 && + config$component %in% c("additive")) { # replace all MZ twin IDs with the first twin's ID in each pair so they are merged for the path tracing and all subsequent steps. We will copy the values back to the second twin at the end. for (id_pair in mz_id_pairs) { @@ -160,7 +161,6 @@ ped2com <- function(ped, component, ped$momID[ped$momID == twin2_id] <- twin1_id ped$dadID[ped$dadID == twin2_id] <- twin1_id } - if (config$verbose == TRUE) { message("Merged ", length(mz_pairs), " MZ twin pair(s) in pedigree dataset for path tracing") } @@ -376,6 +376,12 @@ ped2com <- function(ped, component, r[idx2, ] <- r[idx1, ] r[, idx2] <- r[, idx1] } + # Row/column replacement on a dsCMatrix (symmetric) causes Matrix to + # coerce to dgCMatrix (general), doubling stored entries. Convert back + # so both mz_method paths return the same sparse class. + if (methods::is(r, "CsparseMatrix") && !methods::is(r, "symmetricMatrix")) { + r <- Matrix::forceSymmetric(r) + } if (config$verbose == TRUE) { message("Restored ", length(mz_pairs), " MZ twin pair(s) in relatedness matrix") } From 85b5f196429fd14446dd3589823ef753b4018d35 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 22:47:24 +0000 Subject: [PATCH 3/3] Fix test assertion: use sum(A - B) instead of sum(A, B) sum(A, B) sums all elements of both matrices, which can never be 0 for non-negative relatedness values. Use sum(A - B) to check that the element-wise differences are zero. https://claude.ai/code/session_01PXbgV7bdA9sg7St6xBbg3T --- tests/testthat/test-buildComponent.R | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/testthat/test-buildComponent.R b/tests/testthat/test-buildComponent.R index f827955c..d9da0281 100644 --- a/tests/testthat/test-buildComponent.R +++ b/tests/testthat/test-buildComponent.R @@ -105,9 +105,7 @@ test_that("MZ twins coded at relatedness 1 via twinID column (sparse matrix)", { family_rows <- unique(c(twin_rows, child_rows)) - expect_equal(sum(r_mz1[family_rows, family_rows], r_mz2[family_rows, family_rows]), 0) - - r_mz1[family_rows, family_rows] - r_mz2[family_rows, family_rows] + expect_equal(sum(r_mz1[family_rows, family_rows] - r_mz2[family_rows, family_rows]), 0) r_mz1_ordered <- r_mz1[order(rownames(r_mz1)), order(colnames(r_mz1))] r_mz2_ordered <- r_mz2[order(rownames(r_mz2)), order(colnames(r_mz2))]