App-Anchr

 view release on metacpan or  search on metacpan

lib/App/Anchr/Command/layout.pm  view on Meta::CPAN

package App::Anchr::Command::layout;
use strict;
use warnings;
use autodie;

use App::Anchr -command;
use App::Anchr::Common;

use constant abstract => "layout anthor group";

sub opt_spec {
    return (
        [ "outfile|o=s", "output filename", ],
        [ 'border=i', 'length of borders in anchors', { default => 100 }, ],
        [ "max=i",    "max distance",                 { default => 5000 }, ],
        [ 'pa=s',     'prefix of anchors',            { default => "anchor" }, ],
        [ 'oa=s',     'overlaps between anchors', ],
        [ "png",      "write a png file via graphviz", ],
        { show_defaults => 1, },
    );
}

sub usage_desc {
    return "anchr layout [options] <.ovlp.tsv> <.relation.tsv> <strand.fasta>";
}

sub description {
    my $desc;
    $desc .= ucfirst(abstract) . ".\n";
    return $desc;
}

sub validate_args {
    my ( $self, $opt, $args ) = @_;

    if ( @{$args} != 3 ) {
        my $message = "This command need one or more input files.\n\tIt found";
        $message .= sprintf " [%s]", $_ for @{$args};
        $message .= ".\n";
        $self->usage_error($message);
    }
    for ( @{$args} ) {
        if ( !Path::Tiny::path($_)->is_file ) {
            $self->usage_error("The input file [$_] doesn't exist.");
        }
    }

    if ( $opt->{oa} ) {
        if ( !Path::Tiny::path( $opt->{oa} )->is_file ) {
            $self->usage_error("The overlap file [$opt->{oa}] doesn't exist.\n");
        }
    }

    if ( !exists $opt->{outfile} ) {
        $opt->{outfile} = Path::Tiny::path( $args->[0] )->absolute . ".contig.fasta";
    }
}

sub execute {
    my ( $self, $opt, $args ) = @_;

    #----------------------------#
    # load overlaps and build graph
    #----------------------------#
    my $graph = Graph->new( directed => 1 );
    my %is_anchor;
    my $links_of = {};    # long_id => { anchor_id => overlap_on_long, }
    {
        open my $in_fh, "<", $args->[0];

        my %seen_pair;
        while ( my $line = <$in_fh> ) {
            my $info = App::Anchr::Common::parse_ovlp_line($line);

            # ignore self overlapping
            next if $info->{f_id} eq $info->{g_id};

            # we've orient all sequences to the same strand

lib/App/Anchr/Command/layout.pm  view on Meta::CPAN

                    # f         ----------->
                    # g ========+----------+=======>
                    #          g.B        g.E
                    $graph->add_weighted_edge( $info->{f_id}, $info->{g_id},
                        $info->{g_len} - $info->{g_E} );
                }
            }

            if ( $is_anchor{ $info->{f_id} } and !$is_anchor{ $info->{g_id} } ) {
                my ( $beg, $end ) = App::Anchr::Common::beg_end( $info->{g_B}, $info->{g_E}, );
                $links_of->{ $info->{g_id} }{ $info->{f_id} }
                    = AlignDB::IntSpan->new->add_pair( $beg, $end );
            }
            elsif ( $is_anchor{ $info->{g_id} } and !$is_anchor{ $info->{f_id} } ) {
                my ( $beg, $end ) = App::Anchr::Common::beg_end( $info->{f_B}, $info->{f_E}, );
                $links_of->{ $info->{f_id} }{ $info->{g_id} }
                    = AlignDB::IntSpan->new->add_pair( $beg, $end );
            }

        }
        close $in_fh;
    }

    #----------------------------#
    # Graph of anchors
    #----------------------------#
    my $anchor_graph = Graph->new( directed => 1 );
    {
        my @nodes = $graph->vertices;
        my @linkers = grep { !$is_anchor{$_} } @nodes;

        for my $l (@linkers) {
            my @p = grep { $is_anchor{$_} } $graph->predecessors($l);
            my @s = grep { $is_anchor{$_} } $graph->successors($l);

            for my $p (@p) {
                for my $s (@s) {
                    $anchor_graph->add_edge( $p, $s );
                }
            }

            if ( @p > 1 ) {
                @p = map { $_->[0] }
                    sort { $b->[1] <=> $a->[1] }
                    map { [ $_, $graph->get_edge_weight( $_, $l ) ] } @p;
                for my $i ( 0 .. $#p - 1 ) {
                    $anchor_graph->add_edge( $p[$i], $p[ $i + 1 ] );
                }
            }

            if ( @s > 1 ) {
                @s = map { $_->[0] }
                    sort { $a->[1] <=> $b->[1] }
                    map { [ $_, $graph->get_edge_weight( $l, $_, ) ] } @s;
                for my $i ( 0 .. $#s - 1 ) {
                    $anchor_graph->add_edge( $s[$i], $s[ $i + 1 ] );
                }
            }
        }

        if ( $opt->{png} ) {
            App::Anchr::Common::g2gv( $anchor_graph, $args->[0] . ".png" );
        }
        App::Anchr::Common::transitive_reduction($anchor_graph);
        if ( $opt->{png} ) {
            App::Anchr::Common::g2gv( $anchor_graph, $args->[0] . ".reduced.png" );
        }
    }

    #----------------------------#
    # existing relations
    #----------------------------#
    my $relation_of = {};
    {
        for my $line ( Path::Tiny::path( $args->[1] )->lines( { chomp => 1 } ) ) {
            my @fields = split "\t", $line;

            my $anchor_0   = $fields[0];
            my $anchor_1   = $fields[1];
            my @distances  = split ",", $fields[2];
            my @long_reads = split ",", $fields[3];

            my $pair = join "-", sort ( $anchor_0, $anchor_1, );
            $relation_of->{$pair} = {
                distances  => \@distances,
                long_reads => \@long_reads,
            };
        }
    }

    #----------------------------#
    # loading sequences
    #----------------------------#
    my $seq_of = App::Fasops::Common::read_fasta( $args->[2] );

    #----------------------------#
    # link anchors and break branches
    #----------------------------#
    my @paths;
    if ( $anchor_graph->is_dag ) {
        if ( scalar $anchor_graph->exterior_vertices == 2 ) {
            print STDERR "    Linear\n";

            my @ts = $anchor_graph->topological_sort;
            push @paths, \@ts;
        }
        else {
            print STDERR "    Branched\n";

            my @ts = $anchor_graph->topological_sort;

            my @branchings;
            my %idx_of;
            for my $idx ( 0 .. $#ts ) {
                $idx_of{ $ts[$idx] } = $idx;

                if (   $anchor_graph->out_degree( $ts[$idx] ) > 1
                    or $anchor_graph->in_degree( $ts[$idx] ) > 1 )
                {
                    push @branchings, $ts[$idx];
                }
            }

            $anchor_graph->delete_vertex($_) for @branchings;

            for my $wcc ( $anchor_graph->weakly_connected_components ) {



( run in 1.192 second using v1.01-cache-2.11-cpan-a5162978ef8 )