@@ -1630,6 +1630,134 @@ function hashybridladder(net::HybridNetwork)
1630
1630
return false
1631
1631
end
1632
1632
1633
+ """
1634
+ istreechild(net::HybridNetwork)
1635
+
1636
+ Tuple `(rooted, semi_weakly, semi_strongly)` in which each element is true
1637
+ or false, to indicate if `net` is / is not
1638
+ - tree-child as a rooted network
1639
+ - weakly tree-child as a semidirected network
1640
+ - strongly tree-child as a semidirected network
1641
+
1642
+ A rooted network is tree-child if all of its internal nodes have at least one
1643
+ child that is a tree node (or equivalently, one child edge that is a tree edge).
1644
+ A semidirected network is strongly (resp. weakly) tree-child if all (resp. at
1645
+ least one) of its rooted partners are tree-child.
1646
+
1647
+ Note that degree-2 nodes are not suppressed: a degree-2 node with a single
1648
+ hybrid child causes the network to *not* be tree-child, despite the fact that
1649
+ suppressing this node may render the reduced network tree-child.
1650
+
1651
+ Assumes that tree edges are directly correctly according to the current root.
1652
+ """
1653
+ function istreechild (net:: HybridNetwork )
1654
+ getroot (net). leaf && error (" istreechild requires a root that is not a leaf." )
1655
+ hybparent_visited = Set {Int} ()
1656
+ # not tree-child if 2+ weak nodes, weakly/strongly tree-child if 1/0 weak node
1657
+ hasWatroot = false # did we already find 1 weak node?
1658
+ isTCrooted = true
1659
+ for h in net. hybrid
1660
+ par = getparents (h)
1661
+ for n in par
1662
+ n. number ∈ hybparent_visited && continue
1663
+ push! (hybparent_visited, n. number)
1664
+ ntreechild = 0
1665
+ for e in n. edge
1666
+ if ! e. hybrid && getparent (e) == n
1667
+ ntreechild += 1
1668
+ end
1669
+ end
1670
+ # a hybrid ladder could be okay if polytomy: tree child + hybrid child(ren)
1671
+ if ntreechild > 1 || (n. hybrid && ntreechild > 0 )
1672
+ continue
1673
+ end
1674
+ if n. hybrid # hybrid ladder with no tree child
1675
+ return (false , false , false )
1676
+ end
1677
+ # at this point, n is a tree node
1678
+ atroot = (getroot (net) == n)
1679
+ ntreechild== 1 && ! atroot && continue # incident to 2 tree edges
1680
+ if ntreechild == 0
1681
+ isTCrooted = false
1682
+ # check if no parent tree edge could be a child under another rooting
1683
+ if atroot || ! getparentedge (n). containroot
1684
+ return (false , false , false )
1685
+ end
1686
+ end
1687
+ # now n is a weak node: parent to hybrid edge(s) + incident 1 root component edge
1688
+ # (0 tree child but parent that may contain the root, or root with 1 tree child)
1689
+ if hasWatroot # another weak node was found before
1690
+ ! isTCrooted || error (" the network should have already been flagged as not tree-child" )
1691
+ return (false , false , false )
1692
+ else
1693
+ hasWatroot = true
1694
+ end
1695
+ end
1696
+ end
1697
+ return (isTCrooted, true , ! hasWatroot) # weakly tree-child
1698
+ end
1699
+
1700
+ """
1701
+ isgalled(net::HybridNetwork, checkpreorder::Bool=true)
1702
+
1703
+ `true` (resp. `false`) if `net` is (resp. is not) a galled network, assuming
1704
+ that `net` is bicombining (every hybrid has exactly 2 parents).
1705
+ A network is galled if every hybrid node is contained in a cycle that has its
1706
+ 2 hybrid parent edges and otherwise tree edges only. See for example
1707
+ [Huson, Rupp & Scornavacca (2010)](https://doi.org/10.1017/CBO9780511974076) and
1708
+ [Gunawan, DasGupta & Zhang (2017)](https://doi.org/10.1016/j.ic.2016.11.001).
1709
+ Equivalently, a bicombining network is galled if, for any hybrid node `n`,
1710
+ both of its parent edges originate from the same tree component.
1711
+ We get the tree components by deleting all hybrid edges from the network:
1712
+ we are left with a forest of trees.
1713
+
1714
+ The network is traversed in preorder (from the root to the leaves).
1715
+ Turn `checkpreorder` to `false` if the pre-ordering was run and is known to be
1716
+ up-to-date.
1717
+ Assumption: tree edges are directly correctly according to the current root.
1718
+ """
1719
+ function isgalled (net:: HybridNetwork , checkpreorder:: Bool = true )
1720
+ checkpreorder && preorder! (net)
1721
+ isG = trues (1 ) # mutable
1722
+ # uses a vector node preorder index => integer unique to each tree component
1723
+ # a tree component ID = number of its node
1724
+ traversal_preorder (
1725
+ net. vec_node,
1726
+ init_isgalled, # initialize vector node index => tree component ID
1727
+ traversalupdate_default!, # do nothing at the root
1728
+ updatetree_isgalled,
1729
+ updatehybrid_isgalled,
1730
+ isG
1731
+ )
1732
+ return isG[1 ]
1733
+ end
1734
+ function init_isgalled (nodes:: Vector{Node} , isG)
1735
+ V = zeros (Int,length (nodes))
1736
+ V[1 ] = nodes[1 ]. number # root = 1st in preorder
1737
+ return V
1738
+ end
1739
+ function updatetree_isgalled (V:: Vector , i:: Int , pari:: Int , :: Edge , isG)
1740
+ V[i] = V[pari] # node i is same tree component as its parent
1741
+ return true
1742
+ end
1743
+ function updatehybrid_isgalled (
1744
+ V:: Vector ,
1745
+ i:: Int ,
1746
+ parindx:: AbstractVector{Int} ,
1747
+ paredge:: AbstractVector{Edge} ,
1748
+ isG
1749
+ )
1750
+ # check bicombining, and both parents from same TC
1751
+ length (parindx) == 2 || error (" the network is not bicombining" )
1752
+ if V[parindx[1 ]] != V[parindx[2 ]]
1753
+ isG[1 ] = false
1754
+ return false # stop the traversal
1755
+ else
1756
+ V[i] = getchild (paredge[1 ]). number # start new tree component
1757
+ end
1758
+ return true
1759
+ end
1760
+
1633
1761
"""
1634
1762
shrinkedge!(net::HybridNetwork, edge::Edge)
1635
1763
0 commit comments