← Back to PhyNetPy

PhyNetPy Documentation

Library for the Development and Use of Phylogenetic Network Methods

NetworkMoves Module v1.0.0

The NetworkMoves module provides functions for modifying phylogenetic networks, including adding/removing reticulations, performing NNI operations, and changing node heights. These are the core operations used in network search algorithms.

Author:
Mark Kessler
Last Edit:
3/11/25
Source:
NetworkMoves.py

Overview

The four network moves implemented are:

  1. Add reticulation - Increase network level; can introduce genome duplications (bubbles)
  2. Remove reticulation - Decrease network level
  3. NNI - Change topologies within a given network level
  4. Node height change - Adjust speciation times

add_hybrid

def add_hybrid(net: Network, source: Edge, destination: Edge, t_src: float = None, t_dest: float = None) -> None

Add a hybrid (reticulation) edge from source to destination. Modifies the network in place.

Before:                     After:
src:          dest:         src:          dest:
a             x             a             x
|             |             |             |
|             |             |             |
v             v             v             v
b             y             n1- - - - - ->n2
                            |             |
                            v             v
                            b             y
Parameter Type Description
net Network Network to modify
source Edge Origin edge for hybrid
destination Edge Target edge for hybrid
t_src float Speciation time at n1 (optional)
t_dest float Speciation time at n2 (optional)
Note

If source == destination, a bubble edge (parallel edges) will be created with gamma=0.5 on both edges.

remove_hybrid

def remove_hybrid(net: Network, hybrid_edge: Edge) -> None

Remove a hybrid edge from the network. Modifies the network in place.

Before:                     After:
src:          dest:         src:          dest:
a             x             a             x
|             |             |             |
v             v             v             v
n1- - - - - ->n2            b             y
|             |
v             v
b             y
Parameter Type Description
net Network Network to modify
hybrid_edge Edge Edge whose dest is a reticulation node
Raises: Exception - If edge destination is not a reticulation, or if edge source is a reticulation

nni

def nni(net: Network) -> None

Perform a Nearest Neighbor Interchange (NNI) on the network. Selects a random internal edge and swaps subtrees. Modifies the network in place.

Before:           After (one option):
    a                    a
   / \                  / \
  c   b                d   b
     / \                  / \
    d   e                c   e
Parameter Type Description
net Network Network to modify
Raises: Exception - If no internal edges available or not enough neighbors for NNI

node_height_change

def node_height_change(n: Node, net: Network, height: float, extend: bool = False) -> None

Change the height (speciation time) of a node without altering surrounding node heights. Modifies the network in place.

Parameter Type Description
n Node Node to modify
net Network Network containing the node
height float New height for the node
extend bool If True, retain subtree branch lengths
Constraints

The new height must satisfy: min(child heights) < new_height < max(parent heights)

Raises: NetworkError - If new height is out of bounds or node lacks parents/children

Usage Examples

from PhyNetPy.NetworkMoves import add_hybrid, remove_hybrid, nni, node_height_change
from PhyNetPy.NetworkParser import NetworkParser

# Load a network
parser = NetworkParser("tree.nex")
net = parser.get_network(0)

# Add a hybrid edge between two edges
edges = list(net.E())
source_edge = edges[0]
dest_edge = edges[2]
add_hybrid(net, source_edge, dest_edge)

# Remove a hybrid edge
hybrid_edges = [e for e in net.E() if e.dest.is_reticulation()]
if hybrid_edges:
    remove_hybrid(net, hybrid_edges[0])

# Perform NNI
try:
    nni(net)
except Exception as e:
    print(f"NNI failed: {e}")

# Change node height
internal_nodes = [n for n in net.V() if n not in net.get_leaves() and n != net.root()]
if internal_nodes:
    node = internal_nodes[0]
    current_height = node.get_time()
    new_height = current_height + 0.1
    try:
        node_height_change(node, net, new_height)
    except Exception as e:
        print(f"Height change failed: {e}")

# Create a bubble (genome duplication)
edge = edges[0]
add_hybrid(net, edge, edge)  # Same edge = bubble

See Also

Navigation

Modules

This Page