Computational Genetic Genealogy

Complex Relationship Patterns

Lab 24: Complex Relationship Patterns

Core Component: This lab explores how Bonsai v3 handles complex relationship patterns through the relationships.py module. Understanding these non-standard relationships is essential for accurate pedigree reconstruction in real-world scenarios where family structures often deviate from simple models.

Beyond Standard Relationship Models

The Challenge of Complex Relationships

Real-world genealogy frequently involves complex relationship patterns that don't fit neatly into standard categories like "parent-child" or "first cousins." These complex relationships present several challenges for computational genetic genealogy:

  • Multiple Relationship Paths: Individuals can be related through multiple pathways simultaneously
  • Non-Standard Sharing Patterns: Complex relationships produce DNA sharing patterns that standard models don't account for
  • Recursive Relationships: Some complex patterns involve circular or recursive family connections
  • Representation Challenges: Complex relationships are difficult to represent in traditional pedigree charts

Bonsai v3's relationships.py module provides specialized tools for handling these complex patterns, enabling more accurate pedigree reconstruction in challenging cases.

The relationships.py Module

Core Functionality for Complex Relationships

The relationships.py module in Bonsai v3 provides essential functions for manipulating, combining, and analyzing complex relationship patterns:

from typing import Optional
from .constants import INF

def reverse_rel(
    rel : Optional[tuple[int, int, int]],
):
    """
    Reverse a relationship tuple (u, d, a)
    to (d, u, a) or None to None.

    Args:
        rel: relationship tuple of the form (u, d, a) or None

    Returns:
        rev_rel: (d, u, a) or None
    """
    if type(rel) is tuple:
        rev_rel = (rel[1], rel[0], rel[2])
    else:
        rev_rel = rel
    return rev_rel

def get_deg(
    rel : Optional[tuple[int, int, int]],
):
    """
    Get the degree of a relationship tuple.

    Args:
        rel: relationship tuple of the form (u, d, a), (m, a), or None

    Returns:
        deg: if rel is None or a is None: INF
             else: u + d - a + 1
    """
    if type(rel) is tuple:
        # get m and a
        if len(rel) == 3:
            u,d,a = rel
            m = u+d
        elif len(rel) == 2:
            m, a = rel

        # get degree
        if a is None:
            deg = INF
        elif m == 0:
            deg = 0
        else:
            deg = m - a + 1
    else:
        deg = INF
    return deg
Key Functions in the Module
  • reverse_rel: Reverses a relationship tuple to view it from the opposite perspective
  • get_deg: Calculates the degree of a relationship based on its structure
  • join_rels: Combines two relationships to find an indirect relationship
  • get_transitive_rel: Finds the relationship between the first and last individuals in a chain
  • a_m_to_rel: Converts abstract relationship parameters to concrete relationship tuples
Relationship Representation in Bonsai

Bonsai v3 uses tuple-based representation for relationships:

  • (u, d, a): A relationship with u generations up, d generations down, and a common ancestors
  • For example, (2, 0, 1) represents a grandparent-grandchild relationship (2 generations up, 0 down, 1 common ancestor)
  • (1, 1, 2) represents full siblings (1 up to parents, 1 down, 2 common ancestors)
  • (2, 2, 1) represents first cousins (2 up to grandparents, 2 down, 1 common ancestor)

This flexible representation can encode a wide variety of relationships, including complex ones.

Joining Relationship Paths

Combining Relationships to Find Indirect Connections

One of the key capabilities in handling complex relationships is the ability to join relationship paths to determine how two individuals are connected indirectly. Bonsai implements this through the join_rels function:

def join_rels(
    rel_ab : Optional[tuple[int, int, int]],
    rel_bc : Optional[tuple[int, int, int]],
):
    """
    For three individuals A, B, and C
    related by relAB and relBC, find relAC.

    Args:
        rel_ab: relationship between A and B of the form (u, d, a) or None
        rel_bc: relationship between B and C of the form (u, d, a) or None

    Returns:
        relAC: relationship between A and C of the form (u, d, a) or None
    """
    # Implementation details...

This function enables Bonsai to navigate complex pedigree structures by computing indirect relationships through intermediary individuals. It forms the foundation for understanding multi-path relationships and identifying complex connection patterns.

Example: Relationship Joining

Consider these relationship types:

  • A is parent of B: (0, 1, 1)
  • B is parent of C: (0, 1, 1)

Using join_rels, we can determine that:

  • A is grandparent of C: join_rels((0, 1, 1), (0, 1, 1)) = (0, 2, 1)

This allows Bonsai to build a complete understanding of how individuals are related through multiple pathways.

Transitive Relationships

Finding Relationships Through Chains of Connections

Bonsai extends the joining of relationships to arbitrary chains through the get_transitive_rel function:

def get_transitive_rel(
    rel_list : list[Optional[tuple[int, int, int]]],
):
    """
    For a list of relationships in rel_list that represent
    pairwise relationships from one person to the next in a chain
    of relatives, find the relationship between the first person
    in the list and the last person in the list.

    Args:
        rel_list: chain of relationships of the form [(up, down, num_ancs), ...]
                  where rel_list[i] is the relationship between individuals i and i+1.

    Returns:
        rel: The relationship between individual 0 and individual n specified
             by the chain of relationships in rel_list.
    """
    if rel_list == []:
        return None

    rel = rel_list.pop(0)
    while rel_list:
        next_rel = rel_list.pop(0)
        rel = join_rels(rel, next_rel)

    # ensure that ancestor/descendant relationships
    # only have one ancestor. We can't ensure this
    # within join_rels() so we have to do it here.
    if rel is not None and (rel[0] == 0 or rel[1] == 0):
        rel = (rel[0], rel[1], 1)

    return rel

This capability is essential for understanding how distant individuals are connected through complex family structures and for correctly modeling the expected genetic sharing between indirectly related individuals.

Applications of Transitive Relationships
  • Multi-Generation Analysis: Understanding connections across many generations
  • Complex Pedigree Navigation: Determining how any two individuals in a pedigree are related
  • Validation of Inferred Relationships: Ensuring consistency in relationship chains
  • Identifying Shortest Relationship Paths: Finding the closest connection between individuals

Common Complex Relationship Patterns

Special Cases with Unique Genetic Signatures

Bonsai v3 has specialized handling for several common types of complex relationships that occur in real-world genealogy:

1. Double Relationships

Individuals related through multiple distinct pathways:

  • Double First Cousins: Share all four grandparents (expected sharing ~25%)
  • Half-Siblings Plus First Cousins: Share one parent and are also first cousins through the other parents
  • Double Second Cousins: Related through two different sets of great-grandparents
Modeling Double Relationships

For modeling double relationships, Bonsai combines the expected IBD sharing from both pathways:

# Pseudocode for double relationship modeling
def calculate_expected_sharing_for_double_relationship(rel1, rel2):
    # Get expected sharing for first relationship
    sharing1 = get_expected_sharing(rel1)
    
    # Get expected sharing for second relationship
    sharing2 = get_expected_sharing(rel2)
    
    # For completely independent paths, sharing is additive
    # adjusted to account for overlap
    combined_sharing = sharing1 + sharing2 - (sharing1 * sharing2)
    
    return combined_sharing
2. Consanguineous Relationships

Relationships involving individuals with common ancestors who themselves share common ancestors:

  • Parent-Child with Shared Ancestry: When the parents are themselves related
  • Siblings with Related Parents: When the parents share common ancestry
  • Cousin Marriage Offspring: Children of couples who are related
3. Compound Multi-Generation Relationships

Complex patterns spanning multiple generations:

  • Parallel Cousin Marriages: When multiple cousins from one family marry cousins from another
  • Cross-Generation Marriages: When individuals marry across different generations
  • Pedigree Collapse: When the same ancestor appears in multiple positions in a pedigree
Impact on Genetic Sharing

Complex relationships often produce distinctive patterns of genetic sharing:

  • Elevated Total Sharing: More total DNA shared than expected for the nominal relationship
  • Unusual Segment Patterns: Distinctive patterns of fully identical regions (FIRs) and half-identical regions (HIRs)
  • Increased Homozygosity: More regions where the same DNA variant is inherited from both parents

Bonsai's specialized handling accounts for these patterns to improve relationship inference accuracy.

Handling Endogamy

Relationship Inference in Endogamous Populations

Endogamy—the practice of marrying within a relatively closed community—creates unique challenges for genetic genealogy. Bonsai v3 includes specialized approaches for handling endogamous relationships:

The Endogamy Challenge

In endogamous populations:

  • Background Relatedness: Individuals share more DNA than expected for their genealogical relationship
  • Multiple Paths: Individuals are typically related through many different pathways
  • Model Breakdown: Standard relationship prediction models tend to overestimate closeness
Bonsai's Endogamy Handling
  1. Endogamy Detection: Identifying patterns consistent with endogamous populations
    • Elevated IBD sharing across multiple relationship degrees
    • Distinctive segment length distributions
    • Population-specific markers
  2. Model Adjustment: Adapting relationship inference when endogamy is detected
    • Adjusting expected sharing thresholds
    • Emphasizing segment pattern analysis over total sharing
    • Incorporating population-specific priors
  3. Multi-Path Modeling: Explicitly accounting for multiple relationship paths
    • Decomposing complex relationships into component paths
    • Aggregating sharing expectations across paths
    • Optimizing pedigree structures for path consistency
Adjusting Relationship Inference for Endogamy
# Pseudocode for endogamy-aware relationship inference
def infer_relationship_with_endogamy_awareness(id1, id2, ibd_data, endogamy_factor):
    # Get standard relationship likelihood scores
    standard_rel_scores = calculate_relationship_likelihoods(id1, id2, ibd_data)
    
    # Adjust scores based on endogamy factor
    adjusted_rel_scores = {}
    for rel, score in standard_rel_scores.items():
        # More distant relationships become more likely under endogamy
        adjustment = adjust_for_endogamy(rel, endogamy_factor)
        adjusted_rel_scores[rel] = score * adjustment
    
    # Find the most likely relationship after adjustment
    best_rel = max(adjusted_rel_scores.items(), key=lambda x: x[1])[0]
    
    return {
        "relationship": best_rel,
        "confidence": calculate_confidence(adjusted_rel_scores),
        "endogamy_factor": endogamy_factor,
        "note": "Adjusted for endogamy" if endogamy_factor > 1 else ""
    }

Relationship Degree and Classification

Standardizing Relationship Descriptions

To provide consistent relationship descriptions, Bonsai implements classification systems that translate genetic sharing patterns and genealogical connections into standardized relationship categories:

Relationship Degree Calculation

The get_deg function calculates the "degree" of a relationship, which is a measure of genetic distance:

def get_deg(rel: Optional[tuple[int, int, int]]):
    """
    Get the degree of a relationship tuple.
    
    Args:
        rel: relationship tuple of the form (u, d, a), (m, a), or None
        
    Returns:
        deg: if rel is None or a is None: INF
             else: u + d - a + 1
    """

This degree calculation forms the basis for standardized relationship classification, allowing for consistent terminology even for complex relationships.

Degree Standard Relationship Examples Expected Genetic Sharing
1 Parent-child, full siblings ~50%
2 Grandparent, aunt/uncle, half-sibling ~25%
3 Great-grandparent, first cousin ~12.5%
4 Great-great-grandparent, first cousin once removed ~6.25%
5 Second cousin ~3.125%
Handling Non-Standard Relationships

For complex relationships that don't fit neatly into standard categories, Bonsai implements several approaches:

  • Compound Relationship Description: Describing relationships as combinations of standard types
  • Degree-Based Classification: Falling back to numerical degree when standard terms are inadequate
  • Custom Terminology: Specialized terms for common complex patterns
Relationship Ambiguity

Some complex relationships can create fundamental ambiguity in classification:

  • Half-siblings and double first cousins have similar expected sharing (~25%)
  • Aunt/uncle relationships are genetically similar to half-siblings
  • Complex multi-path relationships may not have standard terminology

In these cases, Bonsai provides multiple possible classifications along with confidence scores and explains the potential ambiguity.

Complex Relationship Visualization

Representing Complex Patterns Visually

Visualizing complex relationships presents unique challenges that Bonsai addresses through specialized rendering techniques:

Visualization Challenges
  • Multiple Connection Paths: Showing all paths connecting individuals
  • Layout Complexity: Arranging complex structures clearly
  • Information Overload: Communicating complexity without overwhelming the user
  • Relationship Ambiguity: Representing uncertain or alternative relationships
Visualization Strategies
  1. Multi-Path Highlighting: Using color coding to show different relationship paths
  2. Interactive Simplification: Allowing users to focus on specific relationship aspects
  3. Relationship Annotations: Adding descriptive labels for complex connections
  4. Alternative Views: Providing different visualization perspectives for complex structures
Visualizing a Complex Relationship

When visualizing a pedigree with complex relationships, Bonsai might:

  1. Show the standard family tree view with all connections
  2. Highlight multiple paths between related individuals with different colors
  3. Add annotations explaining relationship complexities
  4. Provide an alternative "relationship path" view showing direct connections

This multi-faceted approach helps users understand complex genetic relationships that might be difficult to comprehend from a single perspective.

Conclusion and Next Steps

Complex relationship patterns are a common feature of real-world genealogy, and handling them effectively is essential for accurate pedigree reconstruction. Bonsai v3's relationships.py module provides sophisticated tools for manipulating, combining, and analyzing these complex patterns, enabling more comprehensive and accurate family structure inference.

By understanding how to join relationship paths, calculate transitive relationships, handle special cases like endogamy, and visualize complex structures, Bonsai can create more realistic and biologically accurate pedigree reconstructions that reflect the true complexity of human family relationships.

In the next lab, we'll explore how Bonsai v3 handles real-world datasets and the practical challenges that arise when applying computational genetic genealogy techniques to diverse populations and data sources.

Interactive Lab Environment

Run the interactive Lab 24 notebook in Google Colab:

Google Colab Environment

Run the notebook in Google Colab for a powerful computing environment with access to Google's resources.

Data will be automatically downloaded from S3 when you run the notebook.

Note: You may need a Google account to save your work in Google Drive.

Open Lab 24 Notebook in Google Colab