# $Id: Node.pm,v 1.23 2006/05/19 02:08:51 rvosa Exp $
package Bio::Phylo::Forest::Node;
use strict;
use Bio::Phylo::Util::IDPool;
use Bio::Phylo::Util::CONSTANT qw(_NODE_ _TREE_ _TAXON_);
use Scalar::Util qw(looks_like_number weaken);
use XML::Simple;
# One line so MakeMaker sees it.
use Bio::Phylo; our $VERSION = $Bio::Phylo::VERSION;
# classic @ISA manipulation, not using 'base'
use vars qw($VERSION @ISA $HAS_BIOPERL_INTERFACE);
@ISA = qw(Bio::Phylo);
# test for interface
my $interface = 'Bio::Tree::NodeI';
eval "require $interface";
$HAS_BIOPERL_INTERFACE = 1 unless $@;
# aliasing for Bio::Tree::NodeI
if ($HAS_BIOPERL_INTERFACE) {
push @ISA, 'Bio::Tree::NodeI';
*add_Descendent =
sub { $_[0]->set_child( $_[1] ); return scalar @{ $_[0]->get_children } };
*each_Descendent =
sub { $_[0]->get_children ? return @{ $_[0]->get_children } : return };
*get_all_Descendents = sub { return @{ $_[0]->get_descendants } };
*is_Leaf = sub { return $_[0]->is_terminal };
*descendent_count = sub { return scalar @{ $_[0]->get_descendants } };
*to_string = sub { return $_[0]->to_xml };
*height = sub { return $_[0]->calc_max_path_to_tips };
*branch_length = sub {
defined $_[1]
? $_[0]->set_branch_length( $_[1] )->get_branch_length
: $_[0]->get_branch_length;
};
*id = sub { $_[1] ? $_[0]->set_name( $_[1] )->get_name : $_[0]->get_name };
*internal_id = sub { return ${ $_[0] } };
*description =
sub { $_[1] ? $_[0]->set_desc( $_[1] )->get_desc : $_[0]->get_desc };
*bootstrap = sub {
$_[1]
? $_[0]->set_generic( 'bootstrap' => $_[1] )->get_generic('bootstrap')
: $_[0]->get_generic('bootstrap');
};
*ancestor =
sub { $_[1] ? $_[0]->set_parent( $_[1] )->get_parent : $_[0]->get_parent };
*invalidate_height = sub { $_[0]->_flush_cache };
*add_tag_value = sub {
$_[0]->set_generic( $_[1] => $_[2] );
return scalar @{ keys %{ $_[0]->get_generic } };
};
*remove_tag = sub { $_[0]->set_generic( $_[1] => undef ); return undef };
*remove_all_tags = sub { $_[0]->set_generic(undef) };
*get_all_tags = sub { return keys %{ $_[0]->get_generic } };
*get_tag_values = sub { return $_[0]->get_generic( $_[1] ) };
*has_tag = sub { $_[0]->get_generic( $_[1] ) ? return 1 : return 0 };
}
{
# inside out class arrays
my @taxon;
my @parent;
my @first_daughter;
my @last_daughter;
my @next_sister;
my @previous_sister;
my @branch_length;
my @generic;
my @cache;
# $fields hashref necessary for object destruction
my $fields = {
'-taxon' => \@taxon,
'-parent' => \@parent,
'-first_daughter' => \@first_daughter,
'-last_daughter' => \@last_daughter,
'-next_sister' => \@next_sister,
'-previous_sister' => \@previous_sister,
'-branch_length' => \@branch_length,
'-generic' => \@generic,
'-cache' => \@cache,
};
=head1 NAME
Bio::Phylo::Forest::Node - The tree node object.
=head1 SYNOPSIS
# some way to get nodes:
use Bio::Phylo::IO;
my $string = '((A,B),C);';
my $forest = Bio::Phylo::IO->parse(
-format => 'newick',
-string => $string
);
# prints 'Bio::Phylo::Forest'
print ref $forest;
foreach my $tree ( @{ $forest->get_entities } ) {
# prints 'Bio::Phylo::Forest::Tree'
print ref $tree;
foreach my $node ( @{ $tree->get_entities } ) {
# prints 'Bio::Phylo::Forest::Node'
print ref $node;
# node has a parent, i.e. is not root
if ( $node->get_parent ) {
$node->set_branch_length(1);
}
# node is root
else {
$node->set_branch_length(0);
}
}
}
=head1 DESCRIPTION
This module defines a node object and its methods. The node is fairly
syntactically rich in terms of navigation, and additional getters are provided to
further ease navigation from node to node. Typical first daughter -> next sister
traversal and recursion is possible, but there are also shrinkwrapped methods
that return for example all terminal descendants of the focal node, or all
internals, etc.
Node objects are inserted into tree objects, although technically the tree
object is only a container holding all the nodes together. Unless there are
orphans all nodes can be reached without recourse to the tree object.
=head1 METHODS
=head2 CONSTRUCTOR
=over
=item new()
Type : Constructor
Title : new
Usage : my $node = Bio::Phylo::Forest::Node->new;
Function: Instantiates a Bio::Phylo::Forest::Node object
Returns : Bio::Phylo::Forest::Node
Args : All optional:
-parent => $parent,
-taxon => $taxon,
-branch_length => 0.423e+2,
-first_daughter => $f_daughter,
-last_daughter => $l_daughter,
-next_sister => $n_sister,
-previous_sister => $p_sister,
-name => 'node_name',
-desc => 'this is a node',
-score => 0.98,
-generic => {
-posterior => 0.98,
-bootstrap => 0.80
}
=cut
sub new {
my $class = shift;
my $self = Bio::Phylo::Forest::Node->SUPER::new(@_);
bless $self, __PACKAGE__;
if (@_) {
my %opt;
eval { %opt = @_; };
if ($@) {
Bio::Phylo::Util::Exceptions::OddHash->throw( error => $@ );
}
else {
while ( my ( $key, $value ) = each %opt ) {
if ( $fields->{$key} ) {
$fields->{$key}->[$$self] = $value;
if ( ref $value && $value->can('_type') ) {
my $type = $value->_type;
if ( $type == _NODE_ || $type == _TAXON_ ) {
weaken( $fields->{$key}->[$$self] );
}
}
delete $opt{$key};
}
}
@_ = %opt;
}
}
return $self;
}
=item new_from_bioperl()
Type : Constructor
Title : new_from_bioperl
Usage : my $node =
Bio::Phylo::Forest::Node->new_from_bioperl(
$bpnode
);
Function: Instantiates a Bio::Phylo::Forest::Node object
from a bioperl node object.
Returns : Bio::Phylo::Forest::Node
Args : An objects that implements Bio::Tree::NodeI
=cut
sub new_from_bioperl {
my ( $class, $bpnode ) = @_;
my $node = __PACKAGE__->new;
$node->set_name( $bpnode->id );
$node->set_branch_length( $bpnode->branch_length );
$node->set_desc( $bpnode->description );
$node->set_generic( 'bootstrap' => $bpnode->bootstrap );
my @k = $bpnode->get_all_tags;
my @v = $bpnode->get_tag_values;
for my $i ( 0 .. $#k ) {
$node->set_generic( $k[$i] => $v[$i] );
}
return $node;
}
=back
=head2 MUTATORS
=over
=item set_taxon()
Type : Mutator
Title : set_taxon
Usage : $node->set_taxon($taxon);
Function: Assigns taxon crossreferenced with node.
Returns : Modified object.
Args : If no argument is given, the currently
assigned taxon is set to undefined. A
valid argument is a Bio::Phylo::Taxa::Taxon
object.
=cut
sub set_taxon {
my ( $self, $taxon ) = @_;
if ( defined $taxon ) {
if ( $taxon->can('_type') && $taxon->_type == _TAXON_ ) {
if ( $self->_get_container
&& $self->_get_container->_get_container
&& $self->_get_container->_get_container->get_taxa )
{
if ( $taxon->_get_container !=
$self->_get_container->_get_container->get_taxa )
{
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error =>
"Attempt to link to taxon from wrong block" );
}
}
$taxon[$$self] = $taxon;
weaken( $taxon[$$self] );
if ( $self->_get_container
&& $self->_get_container->_get_container )
{
$self->_get_container->_get_container->set_taxa(
$taxon->_get_container );
}
}
else {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$taxon\" doesn't look like a taxon" );
}
}
else {
$taxon[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_parent()
Type : Mutator
Title : parent
Usage : $node->set_parent($parent);
Function: Assigns a node's parent.
Returns : Modified object.
Args : If no argument is given, the current
parent is set to undefined. A valid
argument is Bio::Phylo::Forest::Node
object.
=cut
sub set_parent {
my ( $self, $parent ) = @_;
if ($parent) {
my $type;
eval { $type = $parent->_type; };
if ( $@ || $type != _NODE_ ) {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$parent\" is not a valid node object" );
}
else {
$parent[$$self] = $parent;
weaken $parent[$$self];
}
}
else {
$parent[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_first_daughter()
Type : Mutator
Title : set_first_daughter
Usage : $node->set_first_daughter($f_daughter);
Function: Assigns a node's leftmost daughter.
Returns : Modified object.
Args : Undefines the first daughter if no
argument given. A valid argument is
a Bio::Phylo::Forest::Node object.
=cut
sub set_first_daughter {
my ( $self, $first_daughter ) = @_;
if ($first_daughter) {
my $type;
eval { $type = $first_daughter->_type; };
if ( $@ || $type != _NODE_ ) {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$first_daughter\" is not a valid node object" );
}
else {
$first_daughter[$$self] = $first_daughter;
weaken $first_daughter[$$self];
}
}
else {
$first_daughter[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_last_daughter()
Type : Mutator
Title : set_last_daughter
Usage : $node->set_last_daughter($l_daughter);
Function: Assigns a node's rightmost daughter.
Returns : Modified object.
Args : A valid argument consists of a
Bio::Phylo::Forest::Node object. If
no argument is given, the value is
set to undefined.
=cut
sub set_last_daughter {
my ( $self, $last_daughter ) = @_;
if ($last_daughter) {
my $type;
eval { $type = $last_daughter->_type; };
if ( $@ || $type != _NODE_ ) {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$last_daughter\" is not a valid node object" );
}
else {
$last_daughter[$$self] = $last_daughter;
weaken $last_daughter[$$self];
}
}
else {
$last_daughter[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_previous_sister()
Type : Mutator
Title : set_previous_sister
Usage : $node->set_previous_sister($p_sister);
Function: Assigns a node's previous sister (to the left).
Returns : Modified object.
Args : A valid argument consists of
a Bio::Phylo::Forest::Node object.
If no argument is given, the value
is set to undefined.
=cut
sub set_previous_sister {
my ( $self, $previous_sister ) = @_;
if ($previous_sister) {
my $type;
eval { $type = $previous_sister->_type; };
if ( $@ || $type != _NODE_ ) {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw( error =>
"\"$previous_sister\" is not a valid node object" );
}
else {
$previous_sister[$$self] = $previous_sister;
weaken $previous_sister[$$self];
}
}
else {
$previous_sister[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_next_sister()
Type : Mutator
Title : set_next_sister
Usage : $node->set_next_sister($n_sister);
Function: Assigns or retrieves a node's
next sister (to the right).
Returns : Modified object.
Args : A valid argument consists of a
Bio::Phylo::Forest::Node object.
If no argument is given, the
value is set to undefined.
=cut
sub set_next_sister {
my ( $self, $next_sister ) = @_;
if ($next_sister) {
my $type;
eval { $type = $next_sister->_type; };
if ( $@ || $type != _NODE_ ) {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$next_sister\" is not a valid node object" );
}
else {
$next_sister[$$self] = $next_sister;
weaken $next_sister[$$self];
}
}
else {
$next_sister[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_child()
Type : Mutator
Title : set_child
Usage : $node->set_child($child);
Function: Assigns a new child to $node
Returns : Modified object.
Args : A valid argument consists of a
Bio::Phylo::Forest::Node object.
=cut
sub set_child {
my ( $self, $child ) = @_;
if ($child) {
my $type;
eval { $type = $child->_type; };
if ( $@ || $type != _NODE_ ) {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$child\" is not a valid node object" );
}
else {
if ( my $ld = $self->get_last_daughter ) {
$ld->set_next_sister($child);
$child->set_previous_sister($ld);
$self->set_last_daughter($child);
}
elsif ( my $fd = $self->get_first_daughter ) {
$fd->set_next_sister($child);
$child->set_previous_sister($fd);
$self->set_last_daughter($child);
}
else {
$self->set_first_daughter($child);
}
$child->set_parent($self);
}
}
$self->_flush_cache;
return $self;
}
=item set_branch_length()
Type : Mutator
Title : branch_length
Usage : $node->set_branch_length(0.423e+2);
Function: Assigns a node's branch length.
Returns : Modified object.
Args : If no argument is given, the
current branch length is set
to undefined. A valid argument
is a number in any of Perl's formats.
=cut
sub set_branch_length {
my ( $self, $bl ) = @_;
if ( defined $bl && looks_like_number $bl && !ref $bl ) {
$branch_length[$$self] = $bl;
}
elsif ( defined $bl && ( !looks_like_number $bl || ref $bl ) ) {
Bio::Phylo::Util::Exceptions::BadNumber->throw(
error => "Branch length \"$bl\" is a bad number" );
}
elsif ( !defined $bl ) {
$branch_length[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=item set_generic()
Type : Mutator
Title : set_generic
Usage : $node->set_generic( $key => $value );
Function: Attaches a generic key => value pair to $node.
Returns : Modified object.
Args : Comma separated key => value pairs.
=cut
sub set_generic {
my $self = shift;
if (@_) {
my %opt;
eval { %opt = @_; };
if ($@) {
Bio::Phylo::Util::Exceptions::OddHash->throw( error => $@ );
}
else {
while ( my ( $key, $value ) = each %opt ) {
$generic[$$self]->{$key} = $value;
}
}
}
else {
$generic[$$self] = undef;
}
$self->_flush_cache;
return $self;
}
=back
=head2 ACCESSORS
=over
=item get_taxon()
Type : Accessor
Title : get_taxon
Usage : my $taxon = $node->get_taxon;
Function: Retrieves taxon crossreferenced with node.
Returns : Bio::Phylo::Taxa::Taxon
Args : NONE
=cut
sub get_taxon { $taxon[ ${ $_[0] } ] }
=item get_parent()
Type : Accessor
Title : get_parent
Usage : my $parent = $node->get_parent;
Function: Retrieves a node's parent.
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_parent { $parent[ ${ $_[0] } ] }
=item get_first_daughter()
Type : Accessor
Title : get_first_daughter
Usage : my $f_daughter = $node->get_first_daughter;
Function: Retrieves a node's leftmost daughter.
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_first_daughter { $first_daughter[ ${ $_[0] } ] }
=item get_last_daughter()
Type : Accessor
Title : get_last_daughter
Usage : my $l_daughter = $node->get_last_daughter;
Function: Retrieves a node's rightmost daughter.
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_last_daughter { $last_daughter[ ${ $_[0] } ] }
=item get_previous_sister()
Type : Accessor
Title : get_previous_sister
Usage : my $p_sister = $node->get_previous_sister;
Function: Retrieves a node's previous sister (to the left).
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_previous_sister { $previous_sister[ ${ $_[0] } ] }
=item get_next_sister()
Type : Accessor
Title : get_next_sister
Usage : my $n_sister = $node->get_next_sister;
Function: Retrieves a node's next sister (to the right).
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_next_sister { $next_sister[ ${ $_[0] } ] }
=item get_branch_length()
Type : Accessor
Title : get_branch_length
Usage : my $branch_length = $node->get_branch_length;
Function: Retrieves a node's branch length.
Returns : FLOAT
Args : NONE
Comments: Test for "defined($node->get_branch_length)"
for zero-length (but defined) branches. Testing
"if ( $node->get_branch_length ) { ... }"
yields false for zero-but-defined branches!
=cut
sub get_branch_length { $branch_length[ ${ $_[0] } ] }
=item get_ancestors()
Type : Query
Title : get_ancestors
Usage : my @ancestors = @{ $node->get_ancestors };
Function: Returns an array reference of ancestral nodes,
ordered from young to old.
Returns : Array reference of Bio::Phylo::Forest::Node
objects.
Args : NONE
=cut
sub get_ancestors {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my @ancestors;
my $node = $self;
if ( $node = $node->get_parent ) {
while ($node) {
push @ancestors, $node;
$node = $node->get_parent;
}
$self->_store_cache( \@ancestors );
return \@ancestors;
}
else {
$self->_store_cache(undef);
return;
}
}
=item get_sisters()
Type : Query
Title : get_sisters
Usage : my @sisters = @{ $node->get_sisters };
Function: Returns an array reference of sisters,
ordered from left to right.
Returns : Array reference of
Bio::Phylo::Forest::Node objects.
Args : NONE
=cut
sub get_sisters {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my $sisters = $self->get_parent->get_children;
$self->_store_cache($sisters);
return $sisters;
}
=item get_children()
Type : Query
Title : get_children
Usage : my @children = @{ $node->get_children };
Function: Returns an array reference of immediate
descendants, ordered from left to right.
Returns : Array reference of
Bio::Phylo::Forest::Node objects.
Args : NONE
=cut
sub get_children {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my @children;
my $fd = $self->get_first_daughter;
if ($fd) {
while ($fd) {
push @children, $fd;
$fd = $fd->get_next_sister;
}
$self->_store_cache( \@children );
return \@children;
}
else {
$self->_store_cache(undef);
return;
}
}
=item get_descendants()
Type : Query
Title : get_descendants
Usage : my @descendants = @{ $node->get_descendants };
Function: Returns an array reference of
descendants, recursively ordered
breadth first.
Returns : Array reference of
Bio::Phylo::Forest::Node objects.
Args : none.
=cut
sub get_descendants {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my @current = ($self);
my @desc;
while ( $self->_desc(@current) ) {
@current = $self->_desc(@current);
push @desc, @current;
}
$self->_store_cache( \@desc );
return \@desc;
}
=begin comment
Type : Internal method
Title : _desc
Usage : $node->_desc(\@nodes);
Function: Performs recursion for Bio::Phylo::Forest::Node::get_descendants()
Returns : A Bio::Phylo::Forest::Node object.
Args : A Bio::Phylo::Forest::Node object.
Comments: This method works in conjunction with
Bio::Phylo::Forest::Node::get_descendants() - the latter simply calls
the former with a set of nodes, and the former returns their
children. Bio::Phylo::Forest::Node::get_descendants() then calls
Bio::Phylo::Forest::Node::_desc with this set of children, and so on
until all nodes are terminals. A first_daughter ->
next_sister postorder traversal in a single method would
have been more elegant - though not more efficient, in
terms of visited nodes.
=end comment
=cut
sub _desc {
my $self = shift;
my @current = @_;
my @return;
foreach (@current) {
my $children = $_->get_children;
if ($children) {
push @return, @{$children};
}
}
return @return;
}
=item get_terminals()
Type : Query
Title : get_terminals
Usage : my @terminals = @{ $node->get_terminals };
Function: Returns an array reference
of terminal descendants.
Returns : Array reference of
Bio::Phylo::Forest::Node objects.
Args : NONE
=cut
sub get_terminals {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my @terminals;
my $desc = $self->get_descendants;
if ( @{$desc} ) {
foreach ( @{$desc} ) {
if ( $_->is_terminal ) {
push @terminals, $_;
}
}
}
$self->_store_cache( \@terminals );
return \@terminals;
}
=item get_internals()
Type : Query
Title : get_internals
Usage : my @internals = @{ $node->get_internals };
Function: Returns an array reference
of internal descendants.
Returns : Array reference of
Bio::Phylo::Forest::Node objects.
Args : NONE
=cut
sub get_internals {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my @internals;
my $desc = $self->get_descendants;
if ( @{$desc} ) {
foreach ( @{$desc} ) {
if ( $_->is_internal ) {
push @internals, $_;
}
}
}
$self->_store_cache( \@internals );
return \@internals;
}
=item get_mrca()
Type : Query
Title : get_mrca
Usage : my $mrca = $node->get_mrca($other_node);
Function: Returns the most recent common ancestor
of $node and $other_node.
Returns : Bio::Phylo::Forest::Node
Args : A Bio::Phylo::Forest::Node
object in the same tree.
=cut
sub get_mrca {
my ( $self, $other_node ) = @_;
my $self_anc = $self->get_ancestors;
my $other_anc = $other_node->get_ancestors;
for my $i ( 0 .. $#{$self_anc} ) {
for my $j ( 0 .. $#{$other_anc} ) {
if ( ${ $self_anc->[$i] } == ${ $other_anc->[$j] } ) {
return $self_anc->[$i];
}
}
}
return;
}
=item get_leftmost_terminal()
Type : Query
Title : get_leftmost_terminal
Usage : my $leftmost_terminal =
$node->get_leftmost_terminal;
Function: Returns the leftmost
terminal descendant of $node.
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_leftmost_terminal {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my $daughter = $self;
while ($daughter) {
if ( $daughter->get_first_daughter ) {
$daughter = $daughter->get_first_daughter;
}
else {
last;
}
}
$self->_store_cache($daughter);
return $daughter;
}
=item get_rightmost_terminal()
Type : Query
Title : get_rightmost_terminal
Usage : my $rightmost_terminal =
$node->get_rightmost_terminal;
Function: Returns the rightmost
terminal descendant of $node.
Returns : Bio::Phylo::Forest::Node
Args : NONE
=cut
sub get_rightmost_terminal {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my $daughter = $self;
while ($daughter) {
if ( $daughter->get_last_daughter ) {
$daughter = $daughter->get_last_daughter;
}
else {
last;
}
}
$self->_store_cache($daughter);
return $daughter;
}
=item get_generic()
Type : Accessor
Title : get_generic
Usage : my $generic_value = $node->get_generic($key);
# or
my %generic_hash = %{ $node->get_generic };
# such that
$generic_hash{$key} == $generic_value;
Function: Retrieves value of a generic
key/value pair attached to $node,
given $key. If no $key is given,
a reference to the entire hash is
returned.
Returns : A SCALAR string, or a HASH ref
Args : Key/value pairs are stored in a hashref.
If $node->set_generic(posterior => 0.3543)
has been set, the value can be retrieved
using $node->get_generic('posterior'); if
multiple key/value pairs were set, e.g.
$node->set_generic( x => 12, y => 80) and
$node->get_generic is called without arguments,
a hash reference { x => 12, y => 80 } is
returned.
=cut
sub get_generic {
my ( $self, $key ) = @_;
if ($key) {
return $generic[$$self]->{$key};
}
else {
return $generic[$$self];
}
}
=back
=head2 TESTS
=over
=item is_terminal()
Type : Test
Title : is_terminal
Usage : if ( $node->is_terminal ) {
# do something
}
Function: Returns true if node has
no children (i.e. is terminal).
Returns : BOOLEAN
Args : NONE
=cut
sub is_terminal {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
if ( !$self->get_first_daughter ) {
$self->_store_cache(1);
return 1;
}
else {
$self->_store_cache(undef);
return;
}
}
=item is_internal()
Type : Test
Title : is_internal
Usage : if ( $node->is_internal ) {
# do something
}
Function: Returns true if node
has children (i.e. is internal).
Returns : BOOLEAN
Args : NONE
=cut
sub is_internal {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
if ( $self->get_first_daughter ) {
$self->_store_cache(1);
return 1;
}
else {
$self->_store_cache(undef);
return;
}
}
=item is_descendant_of()
Type : Test
Title : is_descendant_of
Usage : if ( $node->is_descendant_of($grandparent) ) {
# do something
}
Function: Returns true if the node is
a descendant of the argument.
Returns : BOOLEAN
Args : putative ancestor - a
Bio::Phylo::Forest::Node object.
=cut
sub is_descendant_of {
my ( $self, $parent ) = @_;
while ($self) {
if ( $self->get_parent ) {
$self = $self->get_parent;
}
else {
return;
}
if ( $self == $parent ) {
return 1;
}
}
}
=item is_ancestor_of()
Type : Test
Title : is_ancestor_of
Usage : if ( $node->is_ancestor_of($grandchild) ) {
# do something
}
Function: Returns true if the node
is an ancestor of the argument.
Returns : BOOLEAN
Args : putative descendant - a
Bio::Phylo::Forest::Node object.
=cut
sub is_ancestor_of {
my ( $self, $child ) = @_;
if ( $child->is_descendant_of($self) ) {
return 1;
}
else {
return;
}
}
=item is_sister_of()
Type : Test
Title : is_sister_of
Usage : if ( $node->is_sister_of($sister) ) {
# do something
}
Function: Returns true if the node is
a sister of the argument.
Returns : BOOLEAN
Args : putative sister - a
Bio::Phylo::Forest::Node object.
=cut
sub is_sister_of {
my ( $self, $sis ) = @_;
if ( $self->get_parent
&& $sis->get_parent
&& $self->get_parent == $sis->get_parent )
{
return 1;
}
else {
return;
}
}
=item is_outgroup_of()
Type : Test
Title : is_outgroup_of
Usage : if ( $node->is_outgroup_of(\@ingroup) ) {
# do something
}
Function: Tests whether the set of
\@ingroup is monophyletic
with respect to the $node.
Returns : BOOLEAN
Args : A reference to an array of
Bio::Phylo::Forest::Node objects;
Comments: This method is essentially the same as
&Bio::Phylo::Forest::Tree::is_monophyletic.
=cut
sub is_outgroup_of {
my ( $outgroup, $nodes ) = @_;
for my $i ( 0 .. $#{$nodes} ) {
for my $j ( ( $i + 1 ) .. $#{$nodes} ) {
my $mrca = $nodes->[$i]->get_mrca( $nodes->[$j] );
return if $mrca->is_ancestor_of($outgroup);
}
}
return 1;
}
=back
=head2 CALCULATIONS
=over
=item calc_path_to_root()
Type : Calculation
Title : calc_path_to_root
Usage : my $path_to_root =
$node->calc_path_to_root;
Function: Returns the sum of branch
lengths from $node to the root.
Returns : FLOAT
Args : NONE
=cut
sub calc_path_to_root {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my $node = $self;
my $path = 0;
while ($node) {
if ( defined $node->get_branch_length ) {
$path += $node->get_branch_length;
}
if ( $node->get_parent ) {
$node = $node->get_parent;
}
else {
last;
}
}
$self->_store_cache($path);
return $path;
}
=item calc_nodes_to_root()
Type : Calculation
Title : calc_nodes_to_root
Usage : my $nodes_to_root =
$node->calc_nodes_to_root;
Function: Returns the number of nodes
from $node to the root.
Returns : INT
Args : NONE
=cut
sub calc_nodes_to_root {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my ( $nodes, $parent ) = ( 0, $self );
while ($parent) {
$nodes++;
$parent = $parent->get_parent;
if ($parent) {
if ( my $cntr = $parent->calc_nodes_to_root ) {
$nodes += $cntr;
last;
}
}
}
$self->_store_cache($nodes);
return $nodes;
}
=item calc_max_nodes_to_tips()
Type : Calculation
Title : calc_max_nodes_to_tips
Usage : my $max_nodes_to_tips =
$node->calc_max_nodes_to_tips;
Function: Returns the maximum number
of nodes from $node to tips.
Returns : INT
Args : NONE
=cut
sub calc_max_nodes_to_tips {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my ( $nodes, $maxnodes ) = ( 0, 0 );
foreach my $child ( @{ $self->get_terminals } ) {
$nodes = 0;
while ( $child && $child != $self ) {
$nodes++;
$child = $child->get_parent;
}
if ( $nodes > $maxnodes ) {
$maxnodes = $nodes;
}
}
$self->_store_cache($maxnodes);
return $maxnodes;
}
=item calc_min_nodes_to_tips()
Type : Calculation
Title : calc_min_nodes_to_tips
Usage : my $min_nodes_to_tips =
$node->calc_min_nodes_to_tips;
Function: Returns the minimum number of
nodes from $node to tips.
Returns : INT
Args : NONE
=cut
sub calc_min_nodes_to_tips {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my ( $nodes, $minnodes );
foreach my $child ( @{ $self->get_terminals } ) {
$nodes = 0;
while ( $child && $child != $self ) {
$nodes++;
$child = $child->get_parent;
}
if ( !$minnodes || $nodes < $minnodes ) {
$minnodes = $nodes;
}
}
$self->_store_cache($minnodes);
return $minnodes;
}
=item calc_max_path_to_tips()
Type : Calculation
Title : calc_max_path_to_tips
Usage : my $max_path_to_tips =
$node->calc_max_path_to_tips;
Function: Returns the path length from
$node to the tallest tip.
Returns : FLOAT
Args : NONE
=cut
sub calc_max_path_to_tips {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my ( $length, $maxlength ) = ( 0, 0 );
foreach my $child ( @{ $self->get_terminals } ) {
$length = 0;
while ( $child && $child != $self ) {
my $branch_length = $child->get_branch_length;
if ( defined $branch_length ) {
$length += $branch_length;
}
$child = $child->get_parent;
}
if ( $length > $maxlength ) {
$maxlength = $length;
}
}
$self->_store_cache($maxlength);
return $maxlength;
}
=item calc_min_path_to_tips()
Type : Calculation
Title : calc_min_path_to_tips
Usage : my $min_path_to_tips =
$node->calc_min_path_to_tips;
Function: Returns the path length from
$node to the shortest tip.
Returns : FLOAT
Args : NONE
=cut
sub calc_min_path_to_tips {
my $self = shift;
my @tmp = $self->_check_cache;
return $tmp[1] if $tmp[0];
my ( $length, $minlength );
foreach my $child ( @{ $self->get_terminals } ) {
$length = 0;
while ( $child && $child != $self ) {
my $branch_length = $child->get_branch_length;
if ( defined $branch_length ) {
$length += $branch_length;
}
$child = $child->get_parent;
}
if ( !$minlength ) {
$minlength = $length;
}
if ( $length < $minlength ) {
$minlength = $length;
}
}
$self->_store_cache($minlength);
return $minlength;
}
=item calc_patristic_distance()
Type : Calculation
Title : calc_patristic_distance
Usage : my $patristic_distance =
$node->calc_patristic_distance($other_node);
Function: Returns the patristic distance
between $node and $other_node.
Returns : FLOAT
Args : Bio::Phylo::Forest::Node
=cut
sub calc_patristic_distance {
my ( $self, $other_node ) = @_;
my $patristic_distance;
my $mrca = $self->get_mrca($other_node);
while ( $self != $mrca ) {
my $branch_length = $self->get_branch_length;
if ( defined $branch_length ) {
$patristic_distance += $branch_length;
}
$self = $self->get_parent;
}
while ( $other_node != $mrca ) {
my $branch_length = $other_node->get_branch_length;
if ( defined $branch_length ) {
$patristic_distance += $branch_length;
}
$other_node = $other_node->get_parent;
}
return $patristic_distance;
}
=item to_xml()
Type : Format converter
Title : to_xml
Usage : my $xml = $obj->to_xml;
Function: Turns the invocant object into an XML string.
Returns : SCALAR
Args : NONE
=cut
sub to_xml {
my $self = shift;
my $class = ref $self;
$class =~ s/^.*:([^:]+)$/$1/g;
$class = lc($class);
my $xml = '<' . $class . ' id="' . $class . $self->get_id . '">';
my $generic = $self->get_generic;
my ( $name, $score, $desc ) =
( $self->get_name, $self->get_score, $self->get_desc );
$xml .= '' . $name . '' if $name;
$xml .= '' . $score . '' if $score;
$xml .= '' . $desc . '' if $desc;
$xml .= XMLout($generic) if $generic && %{$generic};
$xml .= '' . $self->get_branch_length . ''
if defined $self->get_branch_length;
$xml .= ''
if $self->get_parent;
$xml .= '' . $class . '>';
return $xml;
}
=back
=head2 DESTRUCTOR
=over
=item DESTROY()
Type : Destructor
Title : DESTROY
Usage : $phylo->DESTROY
Function: Destroys Phylo object
Alias :
Returns : TRUE
Args : none
Comments: You don't really need this,
it is called automatically when
the object goes out of scope.
=cut
sub DESTROY {
my $self = shift;
foreach ( keys %{$fields} ) {
delete $fields->{$_}->[$$self];
}
$self->SUPER::DESTROY;
return 1;
}
=begin comment
Type : Internal method
Title : _type
Usage : $node->_type;
Function:
Returns : CONSTANT
Args :
=end comment
=cut
sub _type { _NODE_ }
=begin comment
Type : Internal method
Title : _container
Usage : $node->_container;
Function:
Returns : CONSTANT
Args :
=end comment
=cut
sub _container { _TREE_ }
=back
=head1 Bio::Tree::NodeI methods
If Bio::Tree::NodeI is found in @INC, the Bio::Phylo::Forest::Node object
will implement the Bio::Tree::NodeI methods. Consult the L
documentation for details about the following methods.
=over
=item add_Descendent()
=item add_tag_value()
=item ancestor()
=item branch_length()
=item descendent_count()
=item description()
=item each_Descendent()
=item get_all_Descendents()
=item get_all_tags()
=item get_tag_values()
=item has_tag()
=item height()
=item id()
=item internal_id()
=item invalidate_height()
=item is_Leaf()
=item remove_all_tags()
=item remove_tag()
=item to_string()
=back
=head1 SEE ALSO
=over
=item L
This object inherits from L, so the methods defined
therein are also applicable to L objects.
=item L
If you have BioPerl installed, the L will
implement the NodeI interface.
=item L
Also see the manual: L.
=back
=head1 FORUM
CPAN hosts a discussion forum for Bio::Phylo. If you have trouble
using this module the discussion forum is a good place to start
posting questions (NOT bug reports, see below):
L
=head1 BUGS
Please report any bugs or feature requests to C<< bug-bio-phylo@rt.cpan.org >>,
or through the web interface at
L. I will be notified,
and then you'll automatically be notified of progress on your bug as I make
changes. Be sure to include the following in your request or comment, so that
I know what version you're using:
$Id: Node.pm,v 1.23 2006/05/19 02:08:51 rvosa Exp $
=head1 AUTHOR
Rutger A. Vos,
=over
=item email: C<< rvosa@sfu.ca >>
=item web page: L
=back
=head1 ACKNOWLEDGEMENTS
The author would like to thank Jason Stajich for many ideas borrowed
from BioPerl L, and CIPRES
L and FAB* L
for comments and requests.
=head1 COPYRIGHT & LICENSE
Copyright 2005 Rutger A. Vos, All Rights Reserved. This program is free
software; you can redistribute it and/or modify it under the same terms as Perl
itself.
=cut
}
1;