Skip to content

Conversation

@smasongarrison
Copy link
Member

Summary

This PR implements support for modeling monozygotic (MZ) twins in the relatedness matrix by redirecting one twin's parental links to point to the other twin. This causes the adjacency matrix to assign a relatedness of 1.0 between MZ co-twins instead of the default 0.5 for siblings.

Key Changes

  • New addMZtwins() function in R/constructAdjacency.R:

    • Identifies MZ twin pairs from the twinID column in the pedigree
    • When a zygosity column exists, only modifies pairs where both members have zygosity == "MZ"
    • When no zygosity column exists, assumes all twinID pairs are MZ
    • Redirects the second twin's momID and dadID to point to the first twin
    • Returns both the modified pedigree and a list of modified pairs for downstream processing
  • Updated ped2com() function in R/buildComponent.R:

    • Added mz_twins parameter (defaults to FALSE for backward compatibility)
    • Calls addMZtwins() when mz_twins = TRUE and twinID column exists
    • Fixes the diagonal of the relatedness matrix for redirected twins to ensure they have the same self-relatedness as their co-twin
  • Updated ped2add() function in R/buildComponent.R:

    • Added mz_twins parameter and passes it through to ped2com()
  • Enhanced documentation:

    • Updated mz_twins parameter description in ped2com() to explain the new behavior
    • Added comprehensive docstring for addMZtwins() function
  • Comprehensive test coverage in tests/testthat/test-buildComponent.R:

    • Test that MZ twins get relatedness 1.0 when mz_twins = TRUE
    • Test that MZ twins remain at 0.5 when mz_twins = FALSE
    • Test that MZ twins without zygosity column are treated as MZ
    • Test that DZ twins with zygosity column are NOT modified

Implementation Details

The key insight is that by redirecting both a twin's momID and dadID to point to their co-twin, the path tracing algorithm in the adjacency matrix produces two 0.5 entries (one for each parent link) that sum to 1.0, correctly modeling genetic identity between MZ twins. The diagonal fix ensures that the self-relatedness of the redirected twin matches their co-twin's self-relatedness.

https://claude.ai/code/session_01P3RQTYpWtAtheSqi4aPjR5

claude and others added 6 commits February 11, 2026 18:32
Implement addMZtwins() which redirects one MZ co-twin's parent links
to point to the other twin before adjacency matrix construction. This
produces isPar[twin2, twin1] = 1.0 in the sparse matrix (two 0.5
entries summed), so path tracing yields relatedness 1 between MZ pairs.

Users provide a twinID column (and optionally zygosity) and pass
mz_twins=TRUE to ped2add()/ped2com(). DZ twins are left unchanged
when zygosity column is present.

https://claude.ai/code/session_01P3RQTYpWtAtheSqi4aPjR5
The previous approach redirected twin2's parents to twin1 in the pedigree,
which inflated twin2's diagonal (1.5 instead of 1.0) and twin2-to-child
relatedness (0.75 instead of 0.5).

New approach: after path tracing but before tcrossprod, merge twin2's
column into twin1's in the r2 matrix. MZ twins share the same genetic
source, so this correctly produces:
- MZ twin relatedness = 1.0
- Self-relatedness = 1.0 (no inflation)
- Parent-child and all downstream values correct
- No post-hoc diagonal patching needed

https://claude.ai/code/session_01P3RQTYpWtAtheSqi4aPjR5
Both twin columns now get the same normalized values
(r2_merged = (col1 + col2) / sqrt(2)) so both twins remain
present and contribute equally. Produces the same final
relatedness matrix as the zero approach but without erasing
one twin from the genetic source matrix.

https://claude.ai/code/session_01P3RQTYpWtAtheSqi4aPjR5
Temporarily absorb twin2's column into twin1's before tcrossprod,
then copy twin1's row/col back to twin2 afterward. This keeps the
computation correct while ensuring neither twin is erased from the
final relatedness matrix.

https://claude.ai/code/session_01P3RQTYpWtAtheSqi4aPjR5
Treat both "mz" and "MZ" as monozygotic in findMZtwins (zygosity check now uses %in% c("mz","MZ")). Minor formatting tweak to the verbose message. Added unit tests (tests/testthat/test-buildComponent.R) verifying that MZ twins are coded with relatedness 1 when mz_twins=TRUE, that siblings remain 0.5 when mz_twins=FALSE, self-relatedness stays 1, and parent-child relatedness is unchanged.
@smasongarrison smasongarrison force-pushed the claude/fix-mz-twins-coding-79m80 branch from 0945cb9 to 5f113c4 Compare February 11, 2026 23:07
@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 78.72340% with 10 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (mztwins@bb0c759). Learn more about missing BASE report.

Files with missing lines Patch % Lines
R/constructAdjacency.R 71.42% 8 Missing ⚠️
R/buildComponent.R 89.47% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             mztwins     #116   +/-   ##
==========================================
  Coverage           ?   84.35%           
==========================================
  Files              ?       28           
  Lines              ?     4487           
  Branches           ?        0           
==========================================
  Hits               ?     3785           
  Misses             ?      702           
  Partials           ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@smasongarrison smasongarrison merged commit bc9b50e into mztwins Feb 11, 2026
3 of 4 checks passed
@smasongarrison smasongarrison deleted the claude/fix-mz-twins-coding-79m80 branch February 11, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants