File Coverage

GO/Model/Graph.pm
Criterion Covered Total %
statement 330 961 34.3
branch 86 304 28.2
condition 19 100 19.0
subroutine 50 114 43.8
pod 58 93 62.3
total 543 1572 34.5


line stmt bran cond sub pod time code
1             # $Id: Graph.pm,v 1.25 2008/03/27 01:48:43 cmungall Exp $
2             #
3             # This GO module is maintained by Chris Mungall
4             #
5             # see also - http://www.geneontology.org
6             # - http://www.godatabase.org/dev
7             #
8             # You may distribute this module under the same terms as perl itself
9              
10              
11             package GO::Model::Graph;
12              
13             =head1 NAME
14              
15             GO::Model::Graph - a collection of relationships over terms
16              
17             =head1 SYNOPSIS
18              
19             # FETCHING GRAPH FROM FILES
20             use GO::Parser;
21             my $parser = new GO::Parser({handler=>'obj'});
22             $parser->parse("gene_ontology.obo"); # ontology
23             $parser->parse("gene-associations.sgd"); # gene assocs
24             # get L object
25             my $graph = $parser->handler->graph;
26             my $terms = $graph->term_query("/transmembrane/"); # matching terms
27             foreach my $term (@$terms) {
28             # find gene products associated to this term
29             my $assocs = $graph->deep_association_list($term->acc);
30             printf "Term: %s %s\n", $term->acc, $term->name;
31             print " Associations (direct and via transitive closure_\n";
32             foreach my $assoc (@$assocs) {
33             next if $assoc->is_not;
34             printf " Assoc evidence: %s to: %s %s\n",
35             join(';', map {$_->code} @{$assoc->evidence_list}),
36             $assoc->gene_product->xref->as_str,
37             $assoc->gene_product->symbol;
38             }
39             }
40              
41             # -- alternatively, use this code... --
42              
43             # FETCHING FROM DATABASE (requires go-db-perl library)
44             # pretty-printing a subgraph from "nuclear pore"
45             $apph = GO::AppHandle->connect(-dbname=>"$dbname");
46             $term = $apph->get_term({name=>"nuclear pore"});
47             $graph =
48             $apph->get_graph_by_terms([$term], $depth);
49              
50             $it = $graph->create_iterator;
51             # returns a GO::Model::GraphIterator object
52              
53             while (my $ni = $it->next_node_instance) {
54             $depth = $ni->depth;
55             $term = $ni->term;
56             $reltype = $ni->parent_rel->type;
57             printf
58             "%s %8s Term = %s (%s) // number_of_association=%s // depth=%d\n",
59             "----" x $depth,
60             $reltype,
61             $term->name,
62             $term->public_acc,
63             $term->n_associations || 0,
64             $depth;
65             }
66              
67              
68             =head1 DESCRIPTION
69              
70             Object containing Nodes (L objects) and relationships
71             (: objects)
72              
73             this may be either the whole ontology tree, or a subgraph, depending
74             on how the object is instantiated.
75              
76             =head2 ONTOLOGY GRAPH MODEL
77              
78             relationships can be thought of as B or sentences of the form
79              
80             SUBJECT-TERM PREDICATE OBJECT-TERM
81              
82             for example,
83              
84             "dog" IS_A "animal"
85              
86             "G-Protein coupled receptor" IS_A "transmembrane receptor"
87              
88             Statements have a B (i.e. the subject of the
89             sentence/statement), a predicate/relationship-type and an B
90             (i.e. the object of the sentence/statement)
91              
92             Relationships can also be seen as arcs in a directed graph, with the
93             subject being equivalent to the child, and the object equivalent to
94             the parent. The arc is labeled with the predicate/relationship-type.
95              
96             perl doesnt handle bidirectional links between objects too well, so
97             rather than having the relationship object know about the terms or the
98             term know about the realtionships, all the graph info is in the
99             Graph object
100              
101             the Relationship object gives you the accessions of the related terms,
102             use the Graph methods to fetch these actual terms.
103              
104             The idea is to keep the Term & Relationship objects lightweight, and
105             keep the Graph logic in the Graph object. The Graph object is
106             responsible for stuff like making sure that a Term object is not
107             instantiated twice if it can be reached by two different paths.
108              
109             Currently all graphs are acyclic, cyclic graphs may be allowed in the
110             future when such relationships are added to GO/OBOA
111              
112             =head2 TRANSITIVE CLOSURES
113              
114             graph object will calculate transitive closures for you - that is it
115             will follow the path in the graph to the root or to all leafs
116              
117             =head2 ITERATORS
118              
119             Using the create_iterator and iterate methods, you can create
120             "visitors" that will traverse the graph, performing actions along the
121             way. Functional-style programming is encouraged, as the iterature()
122             method allows for the passing of lexical closures:
123              
124             $graph->iterate(sub {$term=shift->term;
125             printf "%s %s\n", $term->acc,$term->name},
126             {direction=>'up',
127             acc=>"GO:0008045"})
128              
129              
130             =head2 SEE ALSO
131              
132             L
133             L
134             L
135             L
136              
137             =cut
138              
139              
140 14     14   16829 use Carp;
  14         27  
  14         1818  
141 14     14   104 use strict;
  14         30  
  14         404  
142 14     14   74 use Exporter;
  14         35  
  14         545  
143 14     14   822 use GO::Utils qw(rearrange max);
  14         1158  
  14         816  
144 14     14   6683 use GO::ObjFactory;
  14         44  
  14         526  
145 14     14   101 use GO::Model::Root;
  14         28  
  14         416  
146 14     14   71 use GO::Model::Term;
  14         26  
  14         681  
147 14     14   11363 use GO::Model::Path;
  14         36  
  14         1345  
148 14     14   6959 use GO::Model::Relationship;
  14         41  
  14         866  
149 14     14   6748 use GO::Model::GraphIterator;
  14         183  
  14         835  
150 14     14   111 use FileHandle;
  14         32  
  14         85  
151 14     14   7805 use Exporter;
  14         33  
  14         554  
152 14     14   80 use Data::Dumper;
  14         36  
  14         775  
153 14     14   77 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
  14         249  
  14         1411  
154              
155 14     14   78 use base qw(GO::Model::Root Exporter);
  14         36  
  14         210601  
156              
157              
158             sub _valid_params {
159 15     15   50 return qw();
160             }
161              
162              
163             =head2 new
164              
165             Usage - $g = GO::Model::Graph->new;
166             Returns - GO::Model::Graph;
167             Args -
168              
169             Normally you would not create a graph object yourself - this is
170             typically done for you by either a L object or a
171             L object
172              
173             =cut
174              
175             sub _initialize {
176 28     28   46 my $self = shift;
177 28         174 $self->SUPER::_initialize(@_);
178 28         271 $self->{nodes_h} = {};
179 28         69 $self->{nodes_a} = {};
180 28         69 $self->{n_children_h} = {};
181 28         62 $self->{n_parents_h} = {};
182 28         138 $self->{child_relationships_h} = {};
183 28         104 $self->{parent_relationships_h} = {};
184 28 100       176 $self->apph(GO::ObjFactory->new) unless $self->apph;
185 28         69 return;
186             }
187              
188             sub clone
189             {
190 0     0 0 0 my ($self) = @_;
191              
192 0         0 my $new_g = GO::Model::Graph->new();
193 0         0 foreach my $key (keys(%$self))
194             {
195 0         0 my $val = $self->{$key};
196 0         0 my $val_ref = ref($val);
197 0         0 my $new_val;
198 0 0       0 if ($val_ref =~ /HASH/i)
    0          
199             {
200 0         0 my %new_obj = %$val;
201 0         0 $new_val = \%new_obj;
202             }
203             elsif ($val_ref =~ /ARRAY/i)
204             {
205 0         0 my @new_obj = @$val;
206 0         0 $new_val = \@new_obj;
207             }
208             else
209             {
210 0         0 $new_val = $val;
211             }
212              
213 0         0 $new_g->{$key} = $new_val;
214             }
215              
216 0         0 return $new_g;
217             }
218              
219              
220             =head2 create_iterator
221              
222             Usage - $it = $graph->create_iterator("GO:0003677")
223             Usage - $it = $graph->create_iterator({acc=>"GO:0008045",
224             direction=>"up"});
225             Returns - GO::Model::GraphIterator;
226             Args - accession no [optional] or GO::Model::Term [optional]
227              
228             makes a L, an object which traverses the
229             graph
230              
231             =cut
232              
233             sub create_iterator {
234 8     8 1 22 my $self = shift;
235 8         17 my $arg = shift;
236            
237 8 50 0     51 my $h = ref($arg) ? ($arg || {}) : {acc=>$arg};
238 8         83 my $it = GO::Model::GraphIterator->new({graph=>$self, %$h});
239 8 100       121 if ($h->{acc}) {
240 1         6 $it->reset_cursor($h->{acc});
241             }
242 8         31 return $it;
243             }
244              
245              
246             =head2 iterate
247              
248             Usage - $graph->iterate(sub {$ni=shift;printf "%s\n", $ni->term->name});
249             Usage - sub mysub {...}; $graph->iterate(\&mysub);
250             Returns -
251             Args - CODE
252              
253             iterates through the graph executing CODE on every
254             L object
255              
256             =cut
257              
258             sub iterate {
259 0     0 1 0 my $self = shift;
260 0         0 my $sub = shift;
261 0         0 my @args = @_;
262              
263 0 0       0 if (!ref($sub)) {
264 0         0 $sub = eval("sub{$sub}");
265             }
266 0 0       0 if (!ref($sub) eq "CODE") {
267 0         0 confess("argument must be CODE not $sub");
268             }
269              
270 0         0 my $it = $self->create_iterator(@args);
271 0         0 $it->no_duplicates(1);
272              
273 0         0 while (my $ni = $it->next_node_instance) {
274 0         0 &$sub($ni);
275             }
276             }
277              
278              
279             =head2 term_filter
280              
281             Alias - node_filter
282             Usage - $terms =
283             $graph->term_filter(sub {shift->term->name =~ /transmembrane/});
284             Usage - sub mysub {...}; $graph->iterate(\&mysub);
285             Returns - ref to an array of GO::Model::Term objects
286             Args - CODE
287              
288             iterates through the graph executing CODE on every
289             L object. If CODE returns true, that
290             node will be returned
291              
292             =cut
293              
294             sub node_filter {
295 5     5 0 10 my $self = shift;
296 5         10 my $sub = shift;
297 5         14 my @args = @_;
298              
299 5 50       21 if (!ref($sub)) {
300 0         0 $sub = eval("sub{$sub}");
301             }
302 5 50       22 if (!ref($sub) eq "CODE") {
303 0         0 confess("argument must be CODE not $sub");
304             }
305              
306 5         26 my $it = $self->create_iterator(@args);
307 5         40 $it->compact(1);
308            
309 5         12 my @nodes = ();
310 5         24 while (my $ni = $it->next_node_instance) {
311 342 100       6346 if (&$sub($ni)) {
312 4         20 push(@nodes, $ni->term);
313             }
314             }
315 5         324 return \@nodes;
316             }
317             *term_filter = \&node_filter;
318              
319             =head2 term_query
320              
321             Usage - $terms = $graph->term_query({name=>'/transmembrane/'});
322             Usage - $terms = $graph->term_query({acc=>'GO:0008045'});
323             Usage - $terms = $graph->term_query('/transmembrane/');
324             Returns - ref to an array of GO::Model::Term objects
325             Args - hashref of constraints
326             OR name constraint as string
327              
328             returns a set of terms matching query constraints. If the constraint
329             value is enclosed in // a regexp match will be performed
330              
331             constraints are ANDed. For more complex queries, use node_filter()
332              
333             =cut
334              
335             sub term_query {
336 5     5 1 64 my $self = shift;
337 5         8 my $constr = shift;
338 5 50       20 if (!ref($constr)) {
339 0         0 $constr = {name=>$constr};
340             }
341             # compile code for speed
342 5         14 my $code =
343             join(' && ',
344             map {
345 5         18 my $v = $constr->{$_};
346 5         10 my $op = 'eq';
347 5 50       23 if ($v =~ /^\/.*\/$/) {
348 0         0 $op = '=~';
349             }
350             else {
351 5         14 $v =~ s/\'/\\\'/g;
352 5         16 $v = "'$v'";
353             }
354 5 50       25 if (GO::Model::Term->new->can($_."_list")) {
355 0         0 sprintf('grep {$_ %s %s} @{$term->%s_list || []}',
356             $op,
357             $v,
358             $_);
359             }
360             else {
361 5         69 sprintf('$term->%s %s %s',
362             $_,
363             $op,
364             $v);
365             }
366             } keys %$constr);
367 5         5536 my $sub =
368             eval "sub { my \$term=shift->term; $code}";
369 5         29 return $self->node_filter($sub);
370             }
371              
372              
373             =head2 subgraph
374              
375             Usage - my $subgraph = $graph->subgraph({acc=>"GO:0008045"});
376             Returns - GO::Model::Graph
377             Args - as term_query()
378              
379             creates a subgraph of the current graph containing the terms
380             returned by a term_query() call and all paths to the root
381              
382             =cut
383              
384             sub subgraph {
385 1     1 1 9 my $self = shift;
386 1         6 my $terms = $self->term_query(@_);
387 1         8 my $subgraph =
388             $self->subgraph_by_terms($terms);
389 1         7 return $subgraph;
390             }
391              
392             =head2 subgraph_by_terms
393              
394             Usage - my $subgraph = $graph->subgraph_by_terms($terms);
395             Usage - my $subgraph = $graph->subgraph_by_terms($terms,{partial=>1});
396             Returns - GO::Model::Graph
397             Args - GO::Model::Term listref
398              
399             creates a subgraph of the current graph containing the specified terms
400              
401             The path-to-top will be calculated for all terms and added to the
402             subgraph, UNLESS the partial option is set; in this case a
403             relationship between
404              
405             =cut
406              
407             sub subgraph_by_terms {
408 1     1 1 2 my $self = shift;
409 1   50     4 my $terms = shift || [];
410 1   50     6 my $opt = shift || {};
411 1         8 my $g = $self->apph->create_graph_obj;
412 1         4 my %done = ();
413 1         3 my %in_set = map {$_->acc=>1} @$terms;
  1         5  
414 1         4 my $partial = $opt->{partial};
415 1         4 foreach my $term (@$terms) {
416 1         6 my $it = $self->create_iterator($term->acc);
417 1 50       5 if ($partial) {
418 0         0 $it->subset_h(\%in_set);
419             }
420 1         7 $it->direction('up');
421 1         7 while (my $ni = $it->next_node_instance) {
422 5         23 my $t = $ni->term;
423 5         25 my $rel = $ni->parent_rel;
424 5 100       22 $g->add_relationship($rel) if $rel;
425             # don't add term twice (but do add rel to term)
426             # don't continue past already-visited term
427 5 50       14 next if $done{$t->acc};
428 5         17 $done{$t->acc} = 1;
429              
430 5         16 $g->add_term($t);
431             }
432             }
433 1         7 return $g;
434             }
435              
436             =head2 get_all_nodes
437              
438             Usage - my $node_listref = $graph->get_all_nodes();
439             Synonyms- get_all_terms
440             Returns - ref to an array of GO::Model::Term objects
441             Args - none
442              
443             The returned array is UNORDERED
444              
445             If you want the returned list ordered (eg breadth first or depth
446             first) use the create_iterator() method to get a GO::Model::GraphIterator
447              
448             See also L
449              
450             =cut
451              
452             sub get_all_nodes {
453 12     12 1 428 my $self = shift;
454 12         91 my ($order) = rearrange([qw(order)], @_);
455            
456 12         30 my @nodes;
457 12 50       44 if (!$order) {
458 12         26 @nodes = values %{$self->{nodes_h}};
  12         267  
459             # @nodes = @{$self->{nodes_a}};
460             }
461             else {
462 0         0 confess("not implemented yet!");
463             }
464              
465 12         56 return \@nodes;
466             }
467             *get_all_terms = \&get_all_nodes;
468              
469              
470             =head2 get_term
471              
472             Usage - my $term = $graph->get_term($acc);
473             Synonyms- get_node
474             Returns - GO::Model::Term
475             Args - id
476              
477             returns a GO::Model::Term object for an accession no.
478             the term must be in the Graph object
479              
480             See also L
481              
482             =cut
483              
484             sub get_term {
485 5216     5216 1 14127 my $self = shift;
486 5216   33     12990 my $acc = shift || confess "you need to provide an accession ID";
487            
488             # be liberal in what we accept - id, hashref or object
489 5216 50       11584 if (ref($acc)) {
490 0 0       0 if (ref($acc) eq "HASH") {
491 0         0 $acc = $acc->{acc};
492             }
493             else {
494 0         0 $acc = $acc->acc;
495             }
496             }
497 5216         17757 return $self->{nodes_a}->{$acc};
498             }
499             *get_node = \&get_term;
500              
501             =head2 get_term_by_name
502              
503             Usage - my $term = $graph->get_term_by_name("blah");
504             Returns - GO::Model::Term
505             Args - string
506              
507             returns a GO::Model::Term object for a name
508             the term must be in the Graph object
509              
510             CASE INSENSITIVE
511              
512             See also L
513              
514             =cut
515              
516             sub get_term_by_name {
517 1     1 1 6 my $self = shift;
518 1   33     5 my $name = shift || confess;
519            
520 1         2 my @terms = grep { lc($_->name) eq lc($name) } @{$self->get_all_terms};
  11         26  
  1         5  
521            
522 1 50       5 if (!@terms > 1) {
523 0         0 confess(">1 term: @terms");
524             }
525 1         4 return $terms[0];
526             }
527             *get_node_by_name = \&get_term_by_name;
528              
529             =head2 get_terms_by_subset
530              
531             Usage - my $term = $graph->get_terms_by_subset("goslim_plant");
532             Returns - GO::Model::Term
533             Args - string
534              
535             returns a GO::Model::Term object for a subset
536             the term must be in the Graph object
537              
538             CASE INSENSITIVE
539              
540             See also L
541              
542             =cut
543              
544             sub get_terms_by_subset {
545 0     0 1 0 my $self = shift;
546 0   0     0 my $subset = shift || confess;
547            
548 0         0 my @terms = grep { $_->in_subset($subset) } @{$self->get_all_terms};
  0         0  
  0         0  
549            
550 0         0 return \@terms;
551             }
552             *get_nodes_by_subset = \&get_terms_by_subset;
553              
554             =head2 get_top_nodes
555              
556             Usage - my $node_listref = $graph->get_top_nodes();
557             Synonyms- get_top_terms
558             Returns - ref to an array of GO::Model::Term objects
559             Args - none
560              
561             usually returns 1 node - the root term
562              
563             See also L
564              
565             =cut
566              
567             sub get_top_nodes {
568 7     7 1 11 my $self = shift;
569 7 100       51 if ($self->{_top_nodes}) {
570 2         6 return $self->{_top_nodes};
571             }
572 5         12 my @topnodes = ();
573 5         11 foreach my $node (@{$self->get_all_nodes}) {
  5         23  
574 290         760 my $parent_rels = $self->get_parent_relationships($node->acc);
575 290         393 my @parent_nodes = ();
576 290         391 foreach my $rel (@$parent_rels) {
577 312         677 my $node = $self->get_term($rel->acc1);
578 312 100       639 if ($node) {
579 302         658 push(@parent_nodes, $node);
580             }
581             }
582 290 100       878 if (!@parent_nodes) {
583 8         27 push(@topnodes, $node);
584             }
585             }
586 5         39 $self->{_top_nodes} = \@topnodes;
587 5         21 return \@topnodes;
588             }
589             *get_top_terms = \&get_top_nodes;
590              
591              
592             =head2 get_leaf_nodes
593              
594             Usage - my $node_listref = $graph->get_top_nodes();
595             Synonyms- get_leaf_terms
596             Returns - ref to an array of GO::Model::Term objects
597             Args - none
598              
599             See also L
600              
601             =cut
602              
603             sub get_leaf_nodes {
604 0     0 1 0 my $self = shift;
605 0 0       0 if ($self->{_leaf_nodes}) {
606 0         0 return $self->{_leaf_nodes};
607             }
608 0         0 my @leafnodes = ();
609 0         0 foreach my $node (@{$self->get_all_nodes}) {
  0         0  
610 0         0 my $child_rels = $self->get_child_relationships($node->acc);
611 0 0       0 if (!@$child_rels) {
612 0         0 push(@leafnodes, $node);
613             }
614             }
615 0         0 $self->{_leaf_nodes} = \@leafnodes;
616 0         0 return \@leafnodes;
617             }
618             *get_leaf_terms = \&get_leaf_nodes;
619              
620              
621              
622             =head2 is_leaf_node
623              
624             Usage - if ($graph->is_leaf_node($acc)) {...}
625             Returns - bool
626             Args - accession str
627              
628             See also L
629              
630             =cut
631              
632             sub is_leaf_node {
633 0     0 1 0 my $self = shift;
634 0         0 my $acc = shift;
635 0         0 my $child_rels = $self->get_child_relationships($acc);
636 0         0 return !@$child_rels;
637             }
638             *is_leaf_term = \&is_leaf_node;
639              
640             =head2 seed_nodes
641              
642             Usage - $nodes = $graph->seed_nodes;
643             Returns - GO::Model::Term listref
644             Args - GO::Model::Term listref [optional]
645              
646             gets/sets the "seed" nodes/terms - these are the terms the Graph is
647             started from, e.g. for building a node ancestory graph, the seed
648             term would be the leaf of this graph, but not term that are expanded
649             or collpased from the ancestory graph.
650              
651             This is mostly relevant if you are fetching your graphs from a
652             database via go-db-perl
653              
654             See also L
655              
656             =cut
657              
658             sub seed_nodes {
659 0     0 1 0 my $self = shift;
660 0 0       0 $self->{_seed_nodes} = shift if @_;
661 0         0 return $self->{_seed_nodes};
662             }
663              
664              
665             =head2 focus_nodes
666              
667             Usage - $nodes = $graph->focus_nodes;
668             Synonyms- focus_terms
669             Returns - GO::Model::Term listref
670             Args - GO::Model::Term listref [optional]
671              
672             gets/sets the "focus" nodes/terms - these are the terms the Graph is
673             centred around; for instance, if the graph was built around a query to
674             "endoplasmic*" all the terms matching this string would be focused
675              
676             This is mostly relevant if you are fetching your graphs from a
677             database via go-db-perl
678              
679             See also L
680              
681             =cut
682              
683             sub focus_nodes {
684 0     0 1 0 my $self = shift;
685 0 0       0 $self->{_focus_nodes} = shift if @_;
686 0         0 return $self->{_focus_nodes};
687             }
688             *focus_terms = \&focus_nodes;
689              
690             =head2 is_focus_node
691              
692             Usage - if ($g->is_focus_node($term)) {..}
693             Returns - bool
694             Args - GO::Model::Term
695              
696             =cut
697              
698             sub is_focus_node {
699 0     0 1 0 my $self = shift;
700 0         0 my $term = shift;
701 0 0       0 if (grep {$_->acc eq $term->acc} @{$self->focus_nodes || []}) {
  0 0       0  
  0         0  
702 0         0 return 1;
703             }
704 0         0 return 0;
705             }
706             *is_focus_term = \&is_focus_node;
707              
708              
709             =head2 add_focus_node
710              
711             Usage - $g->add_focus_node($term)
712             Returns -
713             Args - GO::Model::Term
714              
715             See also L
716              
717             =cut
718              
719             sub add_focus_node {
720 0     0 1 0 my $self = shift;
721 0         0 my $term = shift;
722 0 0       0 if (!$self->is_focus_node($term)) {
723 0         0 push(@{$self->{_focus_nodes}}, $term);
  0         0  
724             }
725             }
726             *add_focus_term = \&add_focus_node;
727              
728              
729             =head2 paths_to_top
730              
731             Usage - my $paths = $graph->paths_to_top("GO:0005045");
732             Returns - arrayref of GO::Model::Path objects
733             Args -
734              
735             See also L
736              
737             =cut
738              
739             #sub FAST_paths_to_top {
740             # my $self= shift;
741             # my $acc = shift;
742             # my %is_ancestor_h = ();
743             # $self->iterate(sub {
744             # my $ni = shift;
745             # $is_ancestor_h->{$ni->term->acc}=1;
746             # return;
747             # },
748             # {acc=>$acc,
749             # direction=>'up'}
750             # );
751             # print "$_\n" foreach keys %is_ancestor_h;
752             # my @root_accs =
753             # grep {!$self->n_parents($_)} (keys %is_ancestor_h);
754             # if (!@root_accs) {
755             # confess("ASSERTION ERROR: No root accs for $acc");
756             # }
757             # if (@root_accs > 1) {
758             # confess("ONTOLOGY ERROR: >1 root for $acc");
759             # }
760             # my $root_acc = shift @root_accs;
761             # my @nodes = ( {acc=>$root_acc,paths=>[]} );
762            
763             # while (@nodes) {
764             # my $node = shift @nodes;
765             # my $curr_acc = $node->{acc};
766             # my $child_rels = $self->get_child_relationships($curr_acc);
767             # foreach my $child_rel (@$child_rels) {
768             # my $child_term = $self->get_term($child_rel->acc2);
769             # my $child_acc = $child_term->acc;
770             # next unless $is_ancestor_h{$child_acc};
771            
772             # }
773             # }
774             # die 'todo';
775             #}
776              
777             sub paths_to_top {
778 2     2 1 30 my $self= shift;
779 2         21 require GO::Model::Path;
780 2         5 my $acc=shift;
781              
782 2         15 my $path = GO::Model::Path->new;
783 2         8 my @nodes = ({path=>$path, acc=>$acc});
784              
785 2         3 my @paths = ();
786 2         7 while (@nodes) {
787 12         15 my $node = shift @nodes;
788 12         35 my $parent_rels = $self->get_parent_relationships($node->{acc});
789             # printf
790             # "$node->{acc} has parents: %s\n",
791             # join("; ", map {$_->acc} @$parents);
792 12 100       23 if (!@$parent_rels) {
793             # print "PUSING PATH $node->{path}\n";
794 4         19 push(@paths, $node->{path});
795             }
796             else {
797 8         13 foreach my $parent_rel (@$parent_rels) {
798 10         27 my $parent = $self->get_term($parent_rel->acc1);
799 10 50       22 if (!$parent) {
800             # dangling parent - do nothing
801             }
802             else {
803 10         26 my $new_path = $node->{path}->duplicate;
804 10         29 $new_path->add_link($parent_rel->type, $parent);
805 10         29 push(@nodes, {path=>$new_path, acc=>$parent->acc});
806             }
807             }
808             }
809             }
810 2         9 return \@paths;
811             }
812              
813             =head2 node_count
814              
815             Usage - my $count = $g->node_count
816             Synonyms- term_count
817             Returns - int
818             Args -
819              
820             returns the number of terms/nodes in the graph
821              
822             See also L
823              
824             =cut
825              
826             sub node_count {
827 1     1 1 9 my $self = shift;
828 1         3 return scalar(@{$self->get_all_nodes});
  1         6  
829             }
830             *term_count = \&node_count;
831              
832             =head2 n_associations
833              
834             Usage - my $count = $g->n_associations($acc);
835             Returns - int
836             Args -
837              
838             if you parsed an association file into this graph, this will return
839             the number of instances attached directly to acc
840              
841             See also L
842             See also L
843              
844             =cut
845              
846             sub n_associations {
847 0     0 1 0 my $self = shift;
848 0         0 my $acc = shift;
849 0         0 my $term = $self->get_term($acc);
850 0 0       0 if ($term) {
851 0         0 return $term->n_associations
852             }
853             else {
854 0         0 confess("Don't have $acc in $self");
855             }
856             }
857              
858              
859             =head2 n_deep_associations
860              
861             Usage - my $count = $g->n_deep_associations($acc);
862             Returns - int
863             Args -
864              
865             if you parsed an association file into this graph, this will return
866             the number of instances attached directly to acc OR to a node subsumed
867             by acc
868              
869             See also L
870             See also L
871              
872              
873             =cut
874              
875             sub n_deep_associations {
876 0     0 1 0 my $self = shift;
877 0         0 my $acc = shift;
878 0         0 my $rcterms = $self->get_recursive_child_terms($acc);
879 0         0 my $cnt = 0;
880 0         0 foreach (@$rcterms){
881 0         0 $cnt+= $self->n_associations($_->acc)
882             }
883 0         0 return $cnt;
884             }
885              
886              
887             =head2 n_children
888              
889             Usage - $n = $graph->n_children('GO:0003677');
890             Synonyms- n_sterms, n_subj_terms, n_subject_terms
891             Returns - int
892             Args -
893              
894             returns the number of DIRECT children/subject/subordinate terms
895             beneath this one
896              
897             =cut
898              
899             sub n_children {
900 0     0 1 0 my $self = shift;
901 0         0 my $acc = shift;
902 0 0       0 if (ref($acc)) {
903 0 0       0 if (ref($acc) eq "HASH") {
904 0         0 confess("illegal argument: $acc");
905             }
906 0         0 $acc = $acc->acc;
907             }
908 0 0       0 my @tl = @{$self->get_child_terms($acc) || []};
  0         0  
909 0 0       0 if (@tl) {
910 0         0 return scalar(@tl);
911             }
912             # if ($self->{trailing_edges}) {
913             # my $edgeh = $self->{trailing_edges}->{$acc};
914             # if ($edgeh) {
915             # return scalar(keys %$edgeh);
916             # }
917             # return 0;
918             # }
919             # else {
920 0 0       0 if (!defined($self->{n_children_h}->{$acc})) {
921 0         0 my $term =
922             $self->get_term($acc);
923 0 0       0 $term || confess("$acc not in graph $self");
924 0         0 my $tl = $term->apph->get_child_terms($term, {acc=>1});
925 0         0 $self->{n_children_h}->{$acc} = scalar(@$tl);
926             }
927 0         0 return $self->{n_children_h}->{$acc};
928             # }
929             }
930             *n_sterms = \&n_children;
931             *n_subj_terms = \&n_children;
932             *n_subject_terms = \&n_children;
933              
934             =head2 n_parents
935              
936             Usage - $n = $graph->n_parents(3677);
937             Synonyms- n_oterms, n_obj_terms, n_object_terms
938             Returns - int
939             Args -
940              
941             returns the number of DIRECT parent/object/superordinate terms
942             above this one
943              
944             =cut
945              
946             sub n_parents {
947 0     0 1 0 my $self = shift;
948 0         0 my $acc = shift;
949 0 0       0 if (ref($acc)) {
950 0 0       0 if (ref($acc) eq "HASH") {
951 0         0 confess("illegal argument: $acc");
952             }
953 0         0 $acc = $acc->acc;
954             }
955 0 0       0 if (!defined($self->{n_parents_h}->{$acc})) {
956 0         0 my $term =
957             $self->get_term($acc);
958 0 0       0 $term || confess("$acc not in graph $self");
959 0         0 my $tl = $term->apph->get_parent_terms($term, {acc=>1});
960 0         0 $self->{n_parents_h}->{$acc} = scalar(@$tl);
961             }
962 0         0 return $self->{n_parents_h}->{$acc};
963             }
964             *n_oterms = \&n_parents;
965             *n_obj_terms = \&n_parents;
966             *n_object_terms = \&n_parents;
967              
968             =head2 association_list
969              
970             Usage - $assocs = $g->association_list('GO:0003677')
971             Returns - listref of GO::Model::Association
972             Args - acc (string)
973              
974             returns a list of association objects B attached to the
975             specified term
976              
977             See also L
978              
979             =cut
980              
981             sub association_list {
982 1843     1843 1 2063 my $self = shift;
983 1843         2486 my $acc = shift;
984 1843         7395 my $term = $self->get_term($acc);
985 1843 100       4742 if (!$term) {
986             # use Data::Dumper;
987             # print Dumper [keys %{$self->{nodes_a}}];
988             # die "no term with acc $acc";
989 829         2829 return undef;
990             }
991 1014         2939 return $term->association_list();
992             }
993              
994              
995             =head2 get_direct_associations
996              
997             Usage -
998             Returns -
999             Args -
1000              
1001             See also L
1002              
1003             =cut
1004              
1005             sub get_direct_associations {
1006 829     829 1 1087 my $self = shift;
1007 829         1192 my $acc = shift;
1008 829 50       3391 $acc = $acc->acc if ref $acc;
1009 829         2697 $self->association_list($acc);
1010             }
1011              
1012              
1013             =head2 deep_association_list
1014              
1015             Usage - $assocs = $g->association_list('GO:0003677')
1016             Returns - listref of GO::Model::Association
1017             Args - acc (string)
1018              
1019             returns a list of association objects B
1020             attached to the specified term. (ie assocs attached to the term or to
1021             terms subsumed by the specified term).
1022              
1023             See also L
1024              
1025             =cut
1026              
1027             sub deep_association_list {
1028 1014     1014 1 2052 my $self = shift;
1029 1014         1288 my $acc = shift;
1030 1014 100       6999 my @accs = @{$self->association_list($acc) || []};
  1014         2134  
1031 872 50       2277 push(@accs,
1032 872 50       1007 map {@{$self->deep_association_list($_->acc)||[]}}
  1014         3211  
1033 1014         2189 @{$self->get_child_terms($acc) || []});
1034 1014         7169 return \@accs;
1035             }
1036              
1037              
1038             =head2 product_list
1039              
1040             Usage - $prods = $g->product_list('GO:0003677')
1041             Returns - listref of GO::Model::GeneProduct
1042             Args - acc (string)
1043              
1044             returns a list of distinct gene product objects B
1045             attached to the specified term.
1046              
1047             See also L
1048              
1049             =cut
1050              
1051             sub product_list {
1052 0     0 1 0 my $self = shift;
1053 0         0 my $acc = shift;
1054 0   0     0 my $assocs = $self->association_list($acc) || [];
1055 0         0 my @prods = ();
1056 0         0 my %ph = ();
1057 0         0 foreach my $assoc (@$assocs) {
1058 0         0 my $gp = $assoc->gene_product;
1059 0 0       0 if (!$ph{$gp->xref->as_str}) {
1060 0         0 push(@prods, $gp);
1061 0         0 $ph{$gp->xref->as_str} = 1;
1062             }
1063             }
1064 0         0 return [@prods];
1065            
1066             }
1067              
1068             =head2 deep_product_list
1069              
1070             Usage - $prods = $g->deep_product_list('GO:0003677')
1071             Returns - listref of GO::Model::GeneProduct
1072             Args - acc (string)
1073              
1074             returns a list of distinct gene product objects B
1075             attached to the specified term. (ie assocs attached to the term or to
1076             terms subsumed by the specified term).
1077              
1078             See also L
1079              
1080             =cut
1081              
1082             sub deep_product_list {
1083 1     1 1 3 my $self = shift;
1084 1         4 my $acc = shift;
1085 1   50     6 my $assocs = $self->deep_association_list($acc) || [];
1086 1         4 my @prods = ();
1087 1         5 my %ph = ();
1088 1         3 foreach my $assoc (@$assocs) {
1089 86         219 my $gp = $assoc->gene_product;
1090 86 100       442 if (!$ph{$gp->xref->as_str}) {
1091 16         36 push(@prods, $gp);
1092 16         147 $ph{$gp->xref->as_str} = 1;
1093             }
1094             }
1095 1         23 return [@prods];
1096            
1097             }
1098              
1099             =head2 deep_product_count
1100              
1101             Usage - $n_prods = $g->deep_product_count('GO:0003677')
1102             Returns - int
1103             Args - acc (string)
1104              
1105             returns a count of distinct gene product objects B
1106             indirectly> attached to the specified term. performs transitive
1107             closure. will not count gene products twice
1108              
1109             See also L
1110              
1111             =cut
1112              
1113             sub deep_product_count {
1114 0     0 1 0 my $self = shift;
1115 0         0 my $acc = shift;
1116 0         0 return scalar(@{$self->deep_product_list($acc)});
  0         0  
1117             }
1118              
1119             =head2 get_relationships
1120              
1121             Usage - my $rel_listref = $graph->get_relationships('GO:0003677');
1122             Returns - ref to an array of GO::Model::Relationship objects
1123             Args - identifier/acc (string)
1124              
1125             returns relationships which concern the specified term; the specified
1126             term can be the subject or object term in the relationship (ie child
1127             or parent)
1128              
1129             See also L
1130              
1131             =cut
1132            
1133             sub get_relationships {
1134 97     97 1 127 my $self = shift;
1135 97   33     198 my $acc = shift || confess("You must specify an acc");
1136 97         192 my $child_rel_l = $self->get_child_relationships($acc);
1137 97         188 my $parent_rel_l = $self->get_parent_relationships($acc);
1138            
1139 97         107 return [@{$child_rel_l}, @{$parent_rel_l}];
  97         122  
  97         311  
1140             }
1141             *get_rels = \&get_relationships;
1142            
1143              
1144             =head2 get_parent_relationships
1145              
1146             Usage - my $rel_listref = $graph->get_parent_relationships('GO:0003677');
1147             Synonym - get_relationships_by_child
1148             Synonym - get_relationships_by_subj
1149             Synonym - get_relationships_by_subject
1150             Synonym - get_obj_relationships
1151             Synonym - get_object_relationships
1152             Returns - ref to an array of GO::Model::Relationship objects
1153             Args - identifier/acc (string)
1154              
1155             See also L
1156              
1157             =cut
1158            
1159             sub get_parent_relationships {
1160 614     614 1 666 my $self = shift;
1161 614   33     1318 my $acc = shift || confess("You must specify an acc");
1162              
1163             # if a term object is specified instead of ascc no, use the acc no
1164 614 50 33     5043 if (ref($acc) && $acc->isa("GO::Model::Term")) {
1165 0         0 $acc = $acc->acc;
1166             }
1167              
1168 614         1213 my $rel_h = $self->{parent_relationships_h}->{$acc};
1169 614         1268 return $self->get_acc_relationships ($rel_h);
1170             }
1171             *get_relationships_by_child = \&get_parent_relationships;
1172             *get_relationships_by_subj = \&get_parent_relationships;
1173             *get_relationships_by_subject = \&get_parent_relationships;
1174             *get_obj_relationships = \&get_parent_relationships;
1175             *get_object_relationships = \&get_parent_relationships;
1176            
1177              
1178             =head2 get_child_relationships
1179              
1180             Usage - my $rel_listref = $graph->get_child_relationships('GO:0003677');
1181             Synonym - get_relationships_by_parent
1182             Synonym - get_relationships_by_obj
1183             Synonym - get_relationships_by_object
1184             Synonym - get_subj_relationships
1185             Synonym - get_subject_relationships
1186             Returns - ref to an array of GO::Model::Relationship objects
1187             Args - identifier/acc (string)
1188              
1189             See also L
1190              
1191             =cut
1192            
1193             sub get_child_relationships {
1194 1618     1618 1 2051 my $self = shift;
1195 1618   33     4466 my $acc = shift || confess("You must specify an acc");
1196              
1197             # if a term object is specified instead of ascc no, use the acc no
1198 1618 50 33     7047 if (ref($acc) && $acc->isa("GO::Model::Term")) {
1199 0         0 $acc = $acc->acc;
1200             }
1201              
1202 1618         3918 my $rel_h = $self->{child_relationships_h}->{$acc};
1203 1618         4727 return $self->get_acc_relationships ($rel_h);
1204             }
1205             *get_relationships_by_parent = \&get_child_relationships;
1206             *get_relationships_by_obj = \&get_child_relationships;
1207             *get_relationships_by_object = \&get_child_relationships;
1208             *get_subj_relationships = \&get_child_relationships;
1209             *get_subject_relationships = \&get_child_relationships;
1210              
1211             =head2 get_all_relationships
1212              
1213             Usage -
1214             Returns - GO::Model::Relationship list
1215             Args -
1216              
1217             returns all the relationships/statements in this graph
1218              
1219             See also L
1220              
1221             =cut
1222              
1223             sub get_all_relationships {
1224 0     0 1 0 my $self = shift;
1225 0         0 my $nl = $self->get_all_nodes;
1226             [
1227 0         0 map {
1228 0         0 values %{$self->{child_relationships_h}->{$_->acc}}
  0         0  
1229             } @$nl
1230             ];
1231             }
1232              
1233             sub get_acc_relationships {
1234 2232     2232 0 2911 my $self = shift;
1235 2232         2606 my $rel_h = shift;
1236              
1237 2232         3567 my $rels = [];
1238 2232         3175 foreach my $acc (keys (%{$rel_h})) {
  2232         10354  
1239 2204         2406 push (@{$rels}, $rel_h->{$acc});
  2204         13242  
1240             }
1241 2232         7917 return $rels;
1242             }
1243              
1244             =head2 get_parent_terms
1245              
1246             Usage - my $term_lref = $graph->get_parent_terms($parent_term->acc);
1247             Synonym - get_obj_terms
1248             Synonym - get_object_terms
1249             Returns - ref to array of GO::Model::Term objs
1250             Args - the accession of the query term
1251              
1252             See also L
1253              
1254             =cut
1255              
1256             sub get_parent_terms {
1257 100     100 1 474 return shift->_get_related_terms_by_type("parent",@_);
1258             }
1259             *get_obj_terms = \&get_parent_terms;
1260             *get_object_terms = \&get_parent_terms;
1261              
1262              
1263             =head2 get_parent_terms_by_type
1264              
1265             Usage -
1266             Synonym - get_obj_terms_by_type
1267             Synonym - get_object_terms_by_type
1268             Returns - ref to array of GO::Model::Term objs
1269             Args - the accession of the query term
1270             - the type by which to constrain relationships
1271              
1272             See also L
1273              
1274             =cut
1275              
1276             sub get_parent_terms_by_type {
1277 1     1 1 6 return shift->_get_related_terms_by_type("parent",@_);
1278             }
1279             *get_obj_terms_by_type = \&get_parent_terms_by_type;
1280             *get_object_terms_by_type = \&get_parent_terms_by_type;
1281              
1282              
1283             =head2 get_recursive_parent_terms
1284              
1285             Title : get_recursive_parent_terms
1286             Usage :
1287             Synonyms: get_recursive_obj_terms
1288             Synonyms: get_recursive_object_terms
1289             Function:
1290             Example :
1291             Returns :
1292             Args : accession of query term
1293              
1294             See also L
1295              
1296             =cut
1297              
1298             sub get_recursive_parent_terms{
1299 1     1 1 2 my $self = shift;
1300 1         3 my $acc = shift;
1301 1         5 $self->get_recursive_parent_terms_by_type($acc, undef, @_);
1302             }
1303             *get_recursive_obj_terms = \&get_recursive_parent_terms;
1304             *get_recursive_object_terms = \&get_recursive_parent_terms;
1305              
1306             =head2 get_recursive_parent_terms_by_type
1307              
1308             Title : get_recursive_parent_terms_by_type
1309             Usage :
1310             Synonyms: get_recursive_obj_terms_by_type
1311             Synonyms: get_recursive_object_terms_by_type
1312             Function:
1313             Example :
1314             Returns :
1315             Args :
1316              
1317             if type is blank, gets all
1318              
1319             See also L
1320              
1321             =cut
1322              
1323             sub get_recursive_parent_terms_by_type {
1324 1     1 1 5 return shift->_get_recursive_related_terms_by_type("parent",@_);
1325             }
1326             *get_recursive_obj_terms_by_type = \&get_recursive_parent_terms_by_type;
1327             *get_recursive_object_terms_by_type = \&get_recursive_parent_terms_by_type;
1328              
1329              
1330             =head2 get_reflexive_parent_terms
1331              
1332             Title : get_reflexive_parent_terms
1333             Usage :
1334             Function:
1335             Example :
1336             Returns :
1337             Args : acc
1338              
1339             returns parent terms plus the term (for acc) itself
1340              
1341             [reflexive transitive closure of relationships in upward direction]
1342              
1343             See also L
1344              
1345             =cut
1346              
1347             sub get_reflexive_parent_terms {
1348 0     0 1 0 my ($self, $acc) = @_;
1349 0         0 my $terms = $self->get_recursive_parent_terms($acc);
1350 0         0 unshift(@$terms, $self->get_term($acc));
1351 0         0 return $terms;
1352             }
1353              
1354             =head2 get_reflexive_parent_terms_by_type
1355              
1356             Title : get_reflexive_parent_terms_by_type
1357             Usage :
1358             Function:
1359             Example :
1360             Returns : listref of terms
1361             Args : acc, type
1362              
1363             closure of relationship including the term itself
1364              
1365             See also L
1366              
1367             =cut
1368              
1369             sub get_reflexive_parent_terms_by_type{
1370 0     0 1 0 my ($self,$acc, $type) = @_;
1371 0         0 my $terms = $self->get_recursive_parent_terms_by_type($acc, $type);
1372 0         0 return [$self->get_term($acc), @$terms];
1373             }
1374              
1375             =head2 get_child_terms
1376              
1377             Usage - my $term_lref = $graph->get_child_terms($parent_term->acc);
1378             Synonym - get_subj_terms
1379             Synonym - get_subject_terms
1380             Returns - ref to array of GO::Model::Term objs
1381             Args -
1382              
1383             See also L
1384              
1385             =cut
1386              
1387             sub get_child_terms {
1388 1029     1029 1 2986 return shift->_get_related_terms_by_type("child",@_);
1389             }
1390             *get_subj_terms = \&get_child_terms;
1391             *get_subject_terms = \&get_child_terms;
1392              
1393             =head2 get_child_terms_by_type
1394              
1395             Synonym - get_subj_terms_by_type
1396             Synonym - get_subject_terms_by_type
1397             Returns - ref to array of GO::Model::Term objs
1398             Args - the accession of the query term
1399             - the type by which to constrain relationships
1400              
1401             See also L
1402              
1403             =cut
1404              
1405             sub get_child_terms_by_type {
1406 0     0 1 0 return shift->_get_related_terms_by_type("child",@_);
1407             }
1408             *get_subj_terms_by_type = \&get_child_terms_by_type;
1409             *get_subject_terms_by_type = \&get_child_terms_by_type;
1410              
1411             =head2 get_recursive_child_terms
1412              
1413             Title : get_recursive_child_terms
1414             Usage :
1415             Synonyms: get_recursive_subj_terms
1416             Synonyms: get_recursive_subject_terms
1417             Function:
1418             Example :
1419             Returns : a reference to an array of L objects
1420             Args : the accession of the query term
1421              
1422              
1423             See also L
1424              
1425             =cut
1426              
1427             sub get_recursive_child_terms{
1428 1     1 1 4 my ($self,$acc, $refl) = @_;
1429 1         5 $self->get_recursive_child_terms_by_type($acc, undef, $refl);
1430             }
1431             *get_recursive_subj_terms = \&get_recursive_child_terms;
1432             *get_recursive_subject_terms = \&get_recursive_child_terms;
1433              
1434             =head2 get_recursive_child_terms_by_type
1435              
1436             Title : get_recursive_child_terms_by_type
1437             Usage :
1438             Synonyms: get_recursive_subj_terms_by_type
1439             Synonyms: get_recursive_subject_terms_by_type
1440             Function:
1441             Example :
1442             Returns : a reference to an array of L objects
1443             Args : accession, type
1444              
1445             if type is blank, gets all
1446              
1447             See also L
1448              
1449             =cut
1450              
1451             sub get_recursive_child_terms_by_type{
1452 1     1 1 4 return shift->_get_recursive_related_terms_by_type("child",@_);
1453             }
1454             *get_recursive_subj_terms_by_type = \&get_recursive_child_terms_by_type;
1455             *get_recursive_subject_terms_by_type = \&get_recursive_child_terms_by_type;
1456              
1457             =head2 _get_recursive_related_terms_by_type
1458              
1459             Title : _get_recursive_related_terms_by_type
1460             Usage :
1461             Function: Obtain all relationships of the given kind and type for the
1462             term identified by its accession, and recursively repeat
1463             this with all parents and children as query for parent and
1464             child relationships, respectively.
1465              
1466             This is an internal method.
1467             Example :
1468             Returns : A reference to an array of L objects.
1469             Args : - the kind of relationship, either "child" or "parent"
1470             - the accession of the term with which to query
1471             - the type to which to constrain relationships (optional,
1472             all types if left undef)
1473             - TRUE if reflexive and FALSE otherwise (default FALSE)
1474              
1475             See also L
1476              
1477             =cut
1478              
1479             sub _get_recursive_related_terms_by_type{
1480 13     13   21 my ($self, $relkind, $acc, $type, $refl) = @_;
1481            
1482             # if a term object is specified instead of ascc no, use the acc no
1483 13 50 33     35 if (ref($acc) && $acc->isa("GO::Model::Term")) {
1484 0         0 $acc = $acc->acc;
1485             }
1486              
1487 13 100       36 my $rels = ($relkind eq "child")
1488             ? $self->get_child_relationships($acc)
1489             : $self->get_parent_relationships($acc);
1490              
1491 13 50       25 if ($type) {
1492 0         0 @$rels = grep { $_->type eq $type; } @$rels;
  0         0  
1493             }
1494              
1495 13         24 my $relmethod = $relkind."_acc";
1496              
1497 11         36 my @pterms =
1498             map {
1499 13         18 my $term = $self->get_term($_->$relmethod());
1500 11         34 my $rps =
1501             $self->_get_recursive_related_terms_by_type($relkind,
1502             $_->$relmethod(),
1503             $type);
1504 11         31 ($term, @$rps);
1505             } @$rels;
1506 13 50       22 if ($refl) {
1507 0         0 @pterms = ($self->get_term($acc), @pterms);
1508             }
1509 13         27 return \@pterms;
1510             }
1511              
1512             =head2 _get_related_terms_by_type
1513              
1514             Usage - my $term_lref = $graph->_get_related_terms_by_type("child",$acc);
1515             Returns - ref to array of GO::Model::Term objs
1516              
1517             Args - the kind of relationship, either "child" or "parent"
1518             - the accession of the term for which to obtain rel.ships
1519             - the type by which to constrain relationships (optional,
1520             defaults to all terms if left undef)
1521              
1522             This is an internal method.
1523              
1524             =cut
1525              
1526             sub _get_related_terms_by_type {
1527 1130     1130   2164 my ($self,$relkind,$acc,$type) = @_;
1528              
1529             # if a term object is specified instead of ascc no, use the acc no
1530 1130 100 66     5929 if (ref($acc) && $acc->isa("GO::Model::Term")) {
1531 1         5 $acc = $acc->acc;
1532             }
1533              
1534 1130 100       3353 my $rels = ($relkind eq "child")
1535             ? $self->get_child_relationships($acc)
1536             : $self->get_parent_relationships($acc);
1537              
1538 1130 100       3595 if ($type) {
1539 1         3 @$rels = grep { $_->type eq $type; } @$rels;
  2         7  
1540             }
1541              
1542 1130         2981 my $relmethod = $relkind."_acc";
1543              
1544 1130         1928 my @term_l = ();
1545 1130         1934 foreach my $r (@$rels) {
1546 1012         3367 my $t = $self->get_term($r->$relmethod());
1547 1012 100       7240 if ($t) {
1548 994         17584 push(@term_l, $t);
1549             }
1550             }
1551 1130         5174 return \@term_l;
1552             }
1553              
1554             =head2 get_parent_accs_by_type
1555              
1556             Usage -
1557             Returns -
1558             Args - acc, type
1559              
1560             =cut
1561              
1562             sub get_parent_accs_by_type {
1563 0     0 1 0 my $self = shift;
1564 0         0 my $term = shift;
1565 0         0 my $type = shift;
1566 0         0 my $rels = $self->get_parent_relationships($term);
1567 0         0 return [map {$_->acc1} grep {lc($_->type) eq lc($type) } @$rels];
  0         0  
  0         0  
1568             }
1569              
1570              
1571             =head2 get_reflexive_parent_accs_by_type
1572              
1573             Title : get_reflexive_parent_accs_by_type
1574             Usage :
1575             Function:
1576             Example :
1577             Returns : listref of terms
1578             Args : acc, type
1579              
1580             closure of relationship including the term itself
1581              
1582             See also L
1583              
1584             =cut
1585              
1586             sub get_reflexive_parent_accs_by_type{
1587 0     0 1 0 my ($self,$acc, $type) = @_;
1588 0         0 my $terms = $self->get_recursive_parent_accs_by_type($acc, $type);
1589 0         0 return [$acc, @$terms];
1590             }
1591              
1592             =head2 get_relationships_between_terms
1593              
1594             Title : get_relationships_between_terms
1595             Usage :
1596             Function:
1597             Example :
1598             Returns : [] of relationships
1599             Args : parent id, child id
1600              
1601             See also L
1602              
1603             =cut
1604              
1605             sub get_relationships_between_terms{
1606 0     0 1 0 my ($self, $acc1, $acc2) = @_;
1607              
1608 0         0 my $child_rels = $self->get_child_relationships($acc1);
1609            
1610 0         0 return [grep {$_->acc2 eq $acc2} @$child_rels];
  0         0  
1611             }
1612              
1613             =head2 get_parent_closure_hash_by_type
1614              
1615             Title : get_parent_closure_hash_by_type
1616             Usage :
1617             Function: given a term-acc and relationship type, will give a hash that
1618             can be used to check if a term X is a parent of term Y
1619             Example :
1620             Returns :
1621             Args :
1622              
1623             keys will be lower-cased
1624              
1625             =cut
1626              
1627             sub get_parent_closure_hash_by_type{
1628 0     0 1 0 my ($self, $acc, $type) = @_;
1629              
1630 0         0 my $parents =
1631             $self->get_reflexive_parent_terms_by_type($acc,
1632             $type);
1633 0         0 return {map {lc($_->name)=>1} @$parents};
  0         0  
1634             }
1635              
1636              
1637             =head2 add_child_relationship
1638              
1639             See also L
1640              
1641             =cut
1642              
1643             sub add_child_relationship {
1644 0     0 1 0 my $self = shift;
1645 0         0 confess("deprecated");
1646 0         0 my ($rel) =
1647             rearrange([qw(term)], @_);
1648              
1649             }
1650              
1651              
1652             =head2 add_parent_relationship
1653              
1654             parent relationships are as valued as child relationships
1655              
1656             See also L
1657              
1658             =cut
1659              
1660             sub add_parent_relationship {
1661 0     0 1 0 my $self = shift;
1662 0         0 confess("deprecated");
1663 0         0 my ($rel) =
1664             rearrange([qw(term)], @_);
1665             }
1666              
1667              
1668             =head2 close_below
1669              
1670             Usage - $graph->close_below(3677);
1671             Returns -
1672             Args - term (as acc or GO::Model::Term object)
1673              
1674             gets rid of everything below a node
1675              
1676             used by AmiGO for when a user closes a term in the graph
1677              
1678             =cut
1679              
1680             sub close_below {
1681 0     0 1 0 my $self = shift;
1682 0         0 my $node = shift;
1683 0         0 my $if_no_parent_to_delete = shift;
1684 0         0 my $acc;
1685 0 0       0 if (ref($node)) {
1686 0 0       0 if (ref($node) eq "ARRAY") {
    0          
1687 0         0 map { $self->close_below($_) } @$node;
  0         0  
1688 0         0 return;
1689             } elsif ($node->isa('GO::Model::Term')) {
1690 0         0 $acc = $node->acc;
1691             } else {
1692 0         0 $acc = $node->{acc};
1693             }
1694             }
1695             else {
1696 0         0 $acc = $node;
1697             }
1698 0         0 my $iter = $self->create_iterator($acc);
1699 0         0 my @togo = ();
1700 0         0 while (my $n = $iter->next_node) {
1701 0 0       0 unless ($n->acc eq $acc) {
1702 0         0 push(@togo, $n);
1703             }
1704             }
1705 0 0       0 my $p = $if_no_parent_to_delete ? $acc : undef;
1706 0         0 foreach my $n (@togo) {
1707 0         0 $self->delete_node($n->acc, $p);
1708             }
1709             }
1710              
1711             # add 2nd optional arg: parent acc for checking if to delete the node -- Shu
1712             # if there are other parent(s), do not delete the node
1713             sub delete_node {
1714 0     0 0 0 my $self = shift;
1715 0         0 my $acc = shift;
1716 0         0 my $p_acc = shift;
1717              
1718             # delete $self->{parent_relationships_h}->{$acc};
1719             # delete $self->{child_relationships_h}->{$acc};
1720              
1721             # Remove the parent relationship, first from our parents...
1722 0   0     0 my $par_rel_hashes = $self->{parent_relationships_h}->{$acc} || {};
1723 0         0 my $par_rels = [grep {$_} values(%$par_rel_hashes)];
  0         0  
1724 0         0 my $par_rel;
1725 0         0 my $other_p = 0;
1726 0         0 foreach $par_rel (@$par_rels) {
1727 0         0 my $par_acc = $par_rel->acc1;
1728 0 0 0     0 if (!$p_acc || $par_acc && $par_acc eq $p_acc) {
      0        
1729 0         0 $self->{child_relationships_h}->{$par_acc}->{$acc} = undef;
1730 0         0 delete $self->{child_relationships_h}->{$par_acc}->{$acc};
1731             } else {
1732 0         0 $other_p++;
1733             }
1734             }
1735             # ... then from ourself
1736 0 0       0 $self->{parent_relationships_h}->{$acc} = undef unless ($other_p);
1737              
1738              
1739             # Remove the child relationship, first from our children...
1740 0   0     0 my $child_rel_hashes = $self->{child_relationships_h}->{$acc} || {};
1741 0         0 my $child_rels = [grep {$_} values(%$child_rel_hashes)];
  0         0  
1742 0         0 my $child_rel;
1743 0         0 foreach $child_rel (@$child_rels) {
1744 0         0 my $child_acc = $child_rel->acc2;
1745 0 0       0 unless ($other_p) {
1746 0         0 $self->{parent_relationships_h}->{$child_acc}->{$acc} = undef;
1747 0         0 delete $self->{parent_relationships_h}->{$child_acc}->{$acc};
1748             }
1749             }
1750             # ... then from ourself
1751 0 0       0 $self->{child_relationships_h}->{$acc} = undef unless ($other_p);
1752              
1753             # Now delete ourself
1754 0 0       0 unless ($other_p) {
1755 0         0 delete $self->{nodes_h}->{$acc};
1756 0         0 $self->{nodes_a}->{$acc} = undef;
1757             }
1758             # This could change the top and leaf nodes, so
1759             # remove the cached values
1760 0         0 $self->{_top_nodes} = undef;
1761 0         0 $self->{_leaf_nodes} = undef;
1762             }
1763             sub category_term {
1764 0     0 0 0 my $self= shift;
1765              
1766 0         0 my $acc=shift;
1767 0         0 my $paths = $self->paths_to_top($acc);
1768 0         0 my $path = $paths->[0];
1769 0 0 0     0 if (!$path || !$path->term_list) {
1770 0         0 return;
1771             }
1772 0 0       0 if ($path->length < 2) {
1773 0         0 return $path->term_list->[-1];
1774             }
1775 0         0 return $path->term_list->[-2];
1776             }
1777              
1778              
1779             =head2 find_roots
1780              
1781             Usage - my $terms = $graph->find_roots;
1782             Returns - arrayref of GO::Model::Term objects
1783             Args -
1784              
1785             All terms withOUT a parent
1786              
1787             See also L
1788              
1789             =cut
1790              
1791             sub find_roots {
1792 1     1 1 8 my $self= shift;
1793 1         14 require GO::Model::Path;
1794            
1795 1         7 my $nodes = $self->get_all_nodes;
1796 1         3 my @roots = ();
1797 1         3 foreach my $node (@$nodes) {
1798 97         215 my $ps = $self->get_parent_terms($node->acc);
1799 97 100       246 if (!@$ps) {
1800 3         7 push(@roots, $node);
1801             }
1802             }
1803 1         10 return \@roots;
1804             }
1805              
1806              
1807             =head2 get_all_products
1808              
1809             Usage -
1810             Returns -
1811             Args -
1812              
1813             See also L
1814              
1815              
1816             =cut
1817              
1818             sub get_all_products {
1819 0     0 1 0 my $self = shift;
1820 0         0 my $nodes = $self->get_all_nodes;
1821 0         0 my @prod_index = ();
1822 0         0 my @prods = ();
1823 0         0 foreach my $n (@$nodes) {
1824 0         0 foreach my $p (@{$n->product_list}) {
  0         0  
1825 0 0       0 if (!$prod_index[$p->id]) {
1826             }
1827             }
1828             }
1829             }
1830              
1831             sub find_path {
1832 0     0 0 0 confess;
1833             }
1834              
1835             sub build_matrix {
1836 0     0 0 0 my $self = shift;
1837            
1838 0         0 my %node_lookup = ();
1839 0         0 my $terms = $self->get_all_nodes;
1840 0         0 foreach my $t (@$terms) {
1841 0         0 $node_lookup{$t->acc} = {$t->acc => 0};
1842 0         0 my $parents = $self->get_parent_terms($t->acc);
1843             # foreach my $p (@$parents) {
1844             # $node_lookup[$t->acc]->{$p->acc} = 1;
1845             # }
1846 0         0 my %h = $self->parent_dist($t->acc);
1847 0         0 foreach my $k (keys %h) {
1848 0         0 $node_lookup{$t->acc}->{$k} = $h{$k};
1849             }
1850             }
1851 0         0 return %node_lookup;
1852             }
1853              
1854             sub parent_dist {
1855 0     0 0 0 my $self = shift;
1856 0         0 my $acc = shift;
1857 0   0     0 my $dist = shift || 0;
1858 0         0 $dist ++;
1859 0         0 my $parents = $self->get_parent_terms($acc);
1860 0         0 my %h = ();
1861 0         0 foreach my $p (@$parents) {
1862 0         0 $h{$p->acc} = $dist;
1863 0         0 my %rh = $self->parent_dist($p->acc, $dist);
1864 0         0 foreach my $k (keys %rh) {
1865             # multiple parentage; take the shortest path
1866 0 0 0     0 if (!defined($h{$k}) ||
1867             $h{$k} > $rh{$k}) {
1868 0         0 $h{$k} = $rh{$k};
1869             }
1870             }
1871             }
1872 0         0 return %h;
1873             }
1874              
1875              
1876             =head2 merge
1877              
1878             Usage - $g->merge($g2);
1879             Returns -
1880             Args - GO::Model::Graph
1881              
1882             merges two graphs
1883              
1884             =cut
1885              
1886             sub merge {
1887 0     0 1 0 my $self = shift;
1888 0         0 my $g2 = shift;
1889              
1890 0         0 foreach my $t (@{$g2->get_all_nodes}) {
  0         0  
1891 0 0       0 if ($self->get_term($t->acc)) {
1892             }
1893             else {
1894 0         0 $self->add_term($t);
1895             }
1896             }
1897 0 0       0 foreach my $t (@{$g2->focus_nodes || []}) {
  0         0  
1898 0         0 $self->add_focus_node($t);
1899             }
1900 0         0 foreach my $r (@{$g2->get_all_relationships}) {
  0         0  
1901             # don't need to worry about duplicates,
1902             # add_relationship unqiuifies
1903 0         0 $self->add_relationship($r);
1904             }
1905             }
1906              
1907              
1908             sub to_lisp {
1909 0     0 0 0 my $self = shift;
1910 0         0 my $term = shift;
1911              
1912 0 0       0 my @parent_rels =
1913 0         0 @{$self->get_parent_relationships($term->acc) || []};
1914              
1915 0         0 my @parents = ();
1916 0         0 my @lisp_isa = ();
1917 0         0 my @lisp_partof = ();
1918             map {
1919 0 0       0 if ($_->is_inheritance) {
  0         0  
1920 0         0 push(@lisp_isa, $self->get_term($_->acc1));
1921             }
1922             else {
1923 0         0 push(@lisp_partof, $self->get_term($_->acc1));
1924             }
1925 0         0 push(@parents, $self->get_term($_->acc1));
1926             } @parent_rels;
1927 0         0 my $lisp =
1928             ["|".$term->lisp_acc."| T ",
1929             [
1930             ["OCELOT::PARENTS ".
1931             (@parents ?
1932 0         0 join("", (map {"|".$_->lisp_acc."| "} @parents)) :
1933             "OCELOT::FRAMES")
1934             ],
1935             ["DESCRIPTION \"".$term->name."\""],
1936             # ["DEFINITION \"".$term->description."\""],
1937 0         0 @lisp_isa ? ["IS-A ".join("",map{"|".$_->lisp_acc."| "} @lisp_isa)] : "",
1938 0 0       0 @lisp_partof ? ["PART-OF ".join("",map{"|".$_->lisp_acc."| "} @lisp_partof)] : "",
    0          
    0          
1939             ],
1940             "NIL",
1941             ];
1942              
1943            
1944 0         0 my $lisp_term = lisp2text($lisp);
1945             }
1946              
1947             sub lisp2text {
1948 0     0 0 0 my $arr = shift;
1949 0         0 my $text = "";
1950 0         0 for (my $i=0; $i<@$arr; $i++) {
1951 0 0       0 if (ref($arr->[$i])) {
1952 0         0 $text.= lisp2text($arr->[$i]);
1953             }
1954             else {
1955 0         0 $text.= $arr->[$i];
1956             }
1957             }
1958 0         0 return "($text)\n";
1959             }
1960              
1961             sub to_ptuples {
1962 0     0 0 0 my $self = shift;
1963 0         0 my ($th, $include, $sort) =
1964             rearrange([qw(tuples include sort)], @_);
1965 0         0 my $it = $self->create_iterator;
1966 0         0 my @stmts = ();
1967 0         0 my %done = ();
1968 0         0 while (my $ni = $it->next_node_instance) {
1969 0         0 my $term = $ni->term;
1970 0 0       0 next if $done{$term->acc};
1971 0         0 push(@stmts, $term->to_ptuples(-tuples=>$th));
1972 0         0 $done{$term->acc} = $term;
1973             }
1974 0         0 my $rels =
1975             $self->get_all_relationships;
1976 0         0 push(@stmts,
1977 0         0 map { $_->to_ptuples(-tuples=>$th) } @$rels);
1978 0 0 0     0 unless ($include && $include->{'-assocs'}) {
1979 0         0 map { printf "$_:$include->{$_};;;\n"; } keys %$include;
  0         0  
1980 0         0 foreach my $t (values %done) {
1981 0   0     0 my $assocs = $t->association_list || [];
1982 0         0 push(@stmts,
1983 0         0 map {$_->to_ptuples(-term=>$t, -tuples=>$th) } @$assocs);
1984             }
1985             }
1986 0         0 return @stmts;
1987             }
1988              
1989             =head2 export
1990              
1991             Usage - $graph->export({format=>$format});
1992             Returns -
1993             Args - opt hash
1994              
1995             writes out the graph in any export format, including obo, go_ont, owl,
1996             png (graphviz) etc
1997              
1998             =cut
1999              
2000             sub export {
2001 0     0 1 0 my $self = shift;
2002 0   0     0 my $opt = shift || {};
2003 0   0     0 my $format = $opt->{format} || 'obo';
2004 0         0 delete $opt->{format};
2005              
2006             # todo: filehandles/files
2007              
2008 0 0       0 if ($format eq 'png') {
    0          
2009              
2010 0         0 require "GO/IO/Dotty.pm";
2011 0         0 my $graphviz =
2012             GO::IO::Dotty::go_graph_to_graphviz( $self,
2013             {node => {shape => 'box'},
2014             %$opt,
2015             });
2016 0         0 print $graphviz->as_png;
2017             }
2018             elsif ($format eq 'go_ont') {
2019             # todo: tidy this up
2020 0         0 $self->to_text_output(-fmt=>'gotext');
2021             }
2022             else {
2023 0         0 my $p = GO::Parser->new({format=>"GO::Parsers::obj_emitter",
2024             handler=>$format});
2025 0         0 $p->emit_graph($self);
2026             }
2027 0         0 return;
2028             }
2029              
2030             =head2 to_xml
2031              
2032             Usage -
2033             Returns -
2034             Args -
2035              
2036             =cut
2037              
2038             sub to_xml {
2039 0     0 1 0 my $self = shift;
2040 0         0 my $fh = shift;
2041 0         0 require "GO/IO/RDFXML.pm";
2042 0         0 my $out = GO::IO::RDFXML->new(-output=>$fh);
2043 0         0 $out->start_document();
2044 0         0 $out->draw_node_graph($self, @_);
2045 0         0 $out->end_document();
2046             }
2047              
2048             sub to_obo {
2049 0     0 0 0 my $self = shift;
2050 0         0 my $fh = shift;
2051 0         0 require "GO/Handlers/OboOutHandler.pm";
2052 0         0 my $out = GO::Handlers::OboOutHandler->new(-output=>$fh);
2053 0         0 $out->g($self);
2054 0         0 $out->out;
2055             }
2056              
2057             sub add_path {
2058 0     0 0 0 my $self = shift;
2059 0         0 my $path = shift;
2060              
2061 0         0 die 'TODO';
2062 0         0 my $links = $path->link_list;
2063 0         0 for (my $i=0; $i<@$links; $i+=2) {
2064 0         0 my $t = $links->[$i+1];
2065 0         0 $self->add_term($t);
2066 0         0 $self->add_relationship(); # TODO
2067             }
2068             }
2069              
2070              
2071             =head2 add_term
2072              
2073             Usage - $g->add_term($term)
2074             Returns -
2075             Args - GO::Model::Term
2076              
2077             =cut
2078              
2079             sub add_term {
2080 1420     1420 1 2420 my $self = shift;
2081 1420         1668 my $term = shift;
2082 1420 50       3823 if (!ref($term)) {
2083 0         0 confess("Term must be either hashref or Term object");
2084             }
2085 1420 100       3047 if (ref($term) eq 'HASH') {
2086             # $term = $self->apph->create_term_obj($term);
2087 6         29 $term = GO::Model::Term->new($term);
2088             }
2089 1420         3170 my $acc = $term->acc;
2090 1420 50       3274 $acc or confess ("$term has no acc");
2091 1420         4823 $self->{nodes_a}->{$acc} = $term;
2092 1420         4005 $self->{nodes_h}->{$acc} = $self->{nodes_a}->{$acc};
2093 1420         3452 $term;
2094             }
2095              
2096             =head2 add_node
2097              
2098             Usage -
2099             Returns -
2100             Args -
2101              
2102             synonym for add_term
2103              
2104             =cut
2105              
2106             *add_node = \&add_term;
2107              
2108             =head2 add_relationship
2109              
2110             Usage - $graph->add_relationship({acc1=>from_id, acc2=>to_id});
2111             Usage - $graph->add_relationship($from_id, $to_id, $type});
2112             Usage - $graph->add_relationship($obj, $subj, $type});
2113             Returns -
2114             Args -
2115              
2116             only one relationship between id1 and id2 is allowed
2117              
2118             See also L
2119              
2120              
2121             =cut
2122              
2123             sub add_relationship {
2124 743     743 1 8267 my $self = shift;
2125 743         1028 my ($rel) = @_;
2126              
2127 743 100       1626 if (ref($rel) eq "HASH") {
2128 1         5 $rel = GO::Model::Relationship->new($rel);
2129             }
2130 743 100       4569 if (UNIVERSAL::isa($rel, "GO::Model::Relationship")) {
2131             }
2132             else {
2133 738         1198 my ($from_id, $to_id, $type) = @_;
2134 738 100       1459 if (ref($from_id)) {
2135 1 50       8 if (UNIVERSAL::isa($from_id, "GO::Model::Term")) {
2136 0         0 my $term1 = $from_id;
2137 0 0       0 if ($term1->acc) {
2138 0         0 $from_id = $term1->acc;
2139             }
2140             else {
2141 0         0 $from_id = sprintf("%s", $term1);
2142             }
2143             }
2144             }
2145 738 50       1396 if (ref($to_id)) {
2146 0 0       0 if (UNIVERSAL::isa($to_id, "GO::Model::Term")) {
2147 0         0 my $term2 = $to_id;
2148 0 0       0 if ($term2->acc) {
2149 0         0 $to_id = $term2->acc;
2150             }
2151             else {
2152 0         0 $to_id = sprintf("%s", $term2);
2153             }
2154             }
2155             }
2156 738 50       1290 $from_id || confess("did not specify a from id, only @_");
2157 738 50       1293 $to_id || confess("did not specify a to id, only @_");
2158 738         4301 $rel = GO::Model::Relationship->new({acc1=>$from_id, acc2=>$to_id});
2159 738   100     3682 $rel->type($type || 'is_a');
2160             }
2161             # if (!ref($rel)) {
2162             # my ($from_id, $to_id, $type) = @_;
2163             # $rel = GO::Model::Relationship->new({acc1=>$from_id, acc2=>$to_id});
2164             # $rel->type($type);
2165             # }
2166             # if (ref($rel) eq "HASH") {
2167             # $rel = GO::Model::Relationship->new($rel);
2168             # }
2169              
2170 743 50       1648 $rel->acc1 || confess($rel);
2171 743 50       2058 $rel->acc2 || confess($rel);
2172              
2173 743         841 if (0 && $rel->complete) {
2174             # EXPERIMENTAL:
2175             # an OWL/DL style logical definition (N+S conditions) is stored in the DAG as
2176             # normal Relationships but with the 'completes' tag set to true
2177             #
2178             # e.g. for a logical def of "larval locomotory behaviour"
2179             # genus: locomotory behavior
2180             # differentia: during larval_stage
2181             #
2182             # we would have 2 Relationships, one an is_a link to locomotory behavior
2183             # the other a during link to larval_stage (eg from fly_anatomy)
2184             # both these would be tagged complete=1
2185             # - note this is in *addition* to existing links (N conditions)
2186             #
2187             # calling this method removes the logical def links and creates
2188             # a logical definition object
2189             my $term = $self->get_term($rel->acc2);
2190             my $ldef = $term->logical_definition;
2191             if (!$ldef) {
2192             $ldef = $self->apph->create_logical_definition_obj;
2193             $term->logical_definition($ldef);
2194             }
2195             my $oacc = $rel->acc1;
2196             my $type = $rel->type;
2197             if ($type ne 'is_a') {
2198             $ldef->add_intersection([$type,$oacc]);
2199             }
2200             else {
2201             $ldef->add_intersection([$oacc]);
2202             }
2203             return;
2204             }
2205              
2206             # add an index going from parent to child
2207 743 100       2004 if (!$self->{child_relationships_h}->{$rel->acc1}) {
2208 311         902 $self->{child_relationships_h}->{$rel->acc1} = {};
2209             }
2210 743         2209 $self->{child_relationships_h}->{$rel->acc1}->{$rel->acc2} = $rel;
2211              
2212             # add an index going from child to parent
2213 743 100       2139 if (!$self->{parent_relationships_h}->{$rel->acc2}) {
2214 632         1930 $self->{parent_relationships_h}->{$rel->acc2} = {};
2215             }
2216 743         2013 $self->{parent_relationships_h}->{$rel->acc2}->{$rel->acc1} = $rel;
2217              
2218             }
2219              
2220             *add_arc = \&add_relationship;
2221              
2222             sub get_term_properties {
2223 0     0 0 0 my $self = shift;
2224 0         0 my $acc = shift;
2225 0 0       0 if (ref($acc)) {
2226             # term obj?
2227 0         0 $acc = $acc->acc;
2228             }
2229 0         0 my $parents = $self->get_recursive_parent_terms_by_type($acc, 'is_a', 1);
2230 0 0       0 return [map {@{$_->property_list || []}} @$parents];
  0         0  
  0         0  
2231             }
2232              
2233             sub get_all_properties {
2234 0     0 0 0 my $self = shift;
2235 0         0 my $terms = $self->get_all_terms;
2236 0 0       0 my @props = map {@{$_->property_list || []}} @$terms;
  0         0  
  0         0  
2237 0         0 my %ph = map {$_->name => $_} @props;
  0         0  
2238 0         0 return [values %ph];
2239             }
2240              
2241             sub cross_product_index {
2242 0     0 0 0 my $self = shift;
2243 0 0       0 $self->{_cross_product_index} = shift if @_;
2244 0 0       0 $self->{_cross_product_index} = {} unless $self->{_cross_product_index};
2245 0         0 return $self->{_cross_product_index};
2246             }
2247              
2248             sub add_cross_product {
2249 0     0 0 0 my $self = shift;
2250 0         0 my $xp_acc = shift;
2251 0         0 my $xp;
2252 0 0       0 if (ref($xp_acc)) {
2253 0         0 $xp = $xp_acc;
2254 0         0 $xp_acc = $xp->xp_acc;
2255             }
2256             else {
2257 0         0 my $parent_acc = shift;
2258 0         0 my $restrs = shift;
2259 0         0 $xp = $self->apph->create_cross_product_obj({xp_acc=>$xp_acc,
2260             parent_acc=>$parent_acc,
2261             restriction_list=>$restrs});
2262             }
2263 0         0 $self->cross_product_index->{$xp_acc} = $xp;
2264 0         0 return $xp;
2265             }
2266              
2267             sub get_cross_product {
2268 0     0 0 0 my $self = shift;
2269 0         0 my $xp_acc = shift;
2270 0         0 return $self->cross_product_index->{$xp_acc};
2271             }
2272              
2273             sub get_term_by_cross_product {
2274 0     0 0 0 my $self = shift;
2275 0         0 my $xp = shift;
2276 0         0 my $cpi = $self->cross_product_index;
2277 0         0 my @xp_accs = keys %$cpi;
2278 0         0 my $term;
2279 0         0 foreach my $xp_acc (@xp_accs) {
2280 0         0 my $curr_xp = $cpi->{$xp_acc};
2281 0 0       0 if ($xp->equals($curr_xp)) {
2282 0         0 $term = $self->get_term($xp_acc);
2283 0         0 last;
2284             }
2285             }
2286 0         0 return $term;
2287             }
2288              
2289             sub create_subgraph_by_term_type {
2290 0     0 0 0 my $self = shift;
2291 0         0 my $tt = shift;
2292            
2293 0         0 my $g = $self->apph->create_graph_obj;
2294 0         0 my $terms = $self->get_all_terms;
2295 0         0 foreach my $t (@$terms) {
2296 0 0       0 next unless $t->type eq $tt;
2297 0         0 $g->add_term($t);
2298 0         0 $g->add_relationship($_)
2299 0         0 foreach @{$self->get_relationships($t->acc)};
2300 0         0 my $xp = $self->get_cross_product($t->acc);
2301 0 0       0 $g->add_cross_product($xp) if $xp;
2302             }
2303 0         0 return $g;
2304             }
2305              
2306             sub add_trailing_edge {
2307 0     0 0 0 my $self = shift;
2308 0         0 my $acc = shift;
2309 0         0 my $id = shift;
2310 0 0       0 if (!$self->{trailing_edges}) {
2311 0         0 $self->{trailing_edges} = {};
2312             }
2313 0 0       0 if (!$self->{trailing_edges}->{$acc}) {
2314 0         0 $self->{trailing_edges}->{$acc} = {};
2315             }
2316 0         0 $self->{trailing_edges}->{$acc}->{$id} = 1;
2317             }
2318              
2319             sub infer_logical_definitions {
2320 0     0 0 0 my $self = shift;
2321 0         0 my $terms = $self->get_all_terms;
2322             $self->infer_logical_definition_for_term($_->acc)
2323 0         0 foreach @$terms;
2324             }
2325              
2326             # EXPERIMENTAL:
2327             # an OWL/DL style logical definition (N+S conditions) is stored in the DAG as
2328             # normal Relationships but with the 'completes' tag set to true
2329             #
2330             # e.g. for a logical def of "larval locomotory behaviour"
2331             # genus: locomotory behavior
2332             # differentia: during larval_stage
2333             #
2334             # we would have 2 Relationships, one an is_a link to locomotory behavior
2335             # the other a during link to larval_stage (eg from fly_anatomy)
2336             # both these would be tagged complete=1
2337             # - note this is in *addition* to existing links (N conditions)
2338             #
2339             # calling this method removes the logical def links and creates
2340             # a logical definition object
2341             sub infer_logical_definition_for_term {
2342 0     0 0 0 my $self = shift;
2343 0         0 my $acc = shift;
2344 0         0 my $term = $self->get_term($acc);
2345 0         0 my $parent_rels = $self->get_parent_relationships($acc);
2346 0         0 my @isects = grep {$_->complete} @$parent_rels;
  0         0  
2347 0 0       0 warn("assertion warning: $acc has 1 logical def link") if @isects == 1;
2348 0 0       0 return unless @isects > 1;
2349 0         0 my $ldef;
2350 0 0       0 if (@isects) {
2351 0         0 $ldef = $self->apph->create_logical_definition_obj;
2352 0         0 $term->logical_definition($ldef);
2353 0         0 foreach my $isect (@isects) {
2354             # hack: todo; test if genuinely anonymous
2355 0         0 my $oacc = $isect->object_acc;
2356 0         0 my $rel = $isect->type;
2357 0 0       0 if ($rel ne 'is_a') {
2358 0         0 $ldef->add_intersection([$_->type,$oacc]);
2359             }
2360             else {
2361 0         0 $ldef->add_intersection([$oacc]);
2362             }
2363             }
2364             }
2365 0         0 return $ldef;
2366            
2367             }
2368              
2369             sub set_category {
2370 0     0 0 0 my ($self, $id, $category) = @_;
2371             }
2372              
2373             sub add_obsolete_pointer {
2374 0     0 0 0 my ($self, $id, $obsolete_id) = @_;
2375             }
2376              
2377             sub add_synonym {
2378 0     0 0 0 my ($self, $id, $synonym) = @_;
2379             }
2380              
2381             sub add_dbxref {
2382 0     0 0 0 my ($self, $id, $dbxref) = @_;
2383             }
2384              
2385              
2386             sub tab {
2387 0     0 0 0 my $tab = shift;
2388 0   0     0 my $tc = shift || " ";
2389 0         0 print $tc x $tab;
2390             }
2391              
2392             sub _rel_prefix {
2393 4     4   7 my $self = shift;
2394 4         7 my $rel = shift;
2395 4         25 my %th = qw(is_a % part_of < develops_from ~ isa % partof < developsfrom ~);
2396 4   33     16 return $th{lc($rel->type)} || '@'.$rel->type.":";
2397             }
2398              
2399              
2400             =head2 add_buckets
2401              
2402             Usage -
2403             Returns -
2404             Args -
2405              
2406             adds bucket terms to non-leaf nodes
2407              
2408             this is useful for making GO slims
2409              
2410             =cut
2411              
2412             sub add_buckets {
2413 0     0 1 0 my $self = shift;
2414 0         0 my ($idspace) =
2415             rearrange([qw(idpsace)], @_);
2416 0         0 my $terms = $self->get_all_nodes;
2417 0         0 my $id = 1;
2418 0   0     0 $idspace = $idspace || "slim_temp_id";
2419 0         0 foreach my $term (@$terms) {
2420 0 0       0 if (!$self->is_leaf_node($term->acc)) {
2421             #printf STDERR "adding bucket for %s\n", $term->acc;
2422 0         0 my $t = $self->apph->create_term_obj;
2423             # start name with Z to force last alphabetic placement
2424 0         0 $t->name("Z-OTHER-".$term->name);
2425 0         0 $t->acc("$idspace:$id");
2426 0         0 $id++;
2427 0         0 $self->add_term($t);
2428 0         0 $self->add_relationship($term,
2429             $t,
2430             "bucket");
2431             }
2432             }
2433 0         0 return;
2434             }
2435              
2436              
2437             =head2 to_text_output
2438              
2439             Usage -
2440             Returns -
2441             Args - fmt, assocs [bool]
2442              
2443             hacky text output
2444              
2445             this method should probably move out of the model code
2446             into output adapters
2447              
2448             =cut
2449              
2450             sub to_text_output {
2451 1     1 1 9 my $self = shift;
2452 1         86 my ($fmt, $show_assocs, $fh, $disp_filter, $it, $opts, $suppress) =
2453             rearrange([qw(fmt assocs fh disp_filter it opts suppress)], @_);
2454              
2455 1   50     9 $fmt = $fmt || "gotext";
2456 1   50     8 $fh = $fh || \*STDOUT;
2457              
2458 1 50       4 $opts = {} unless $opts;
2459 1 50       6 $it = $self->create_iterator unless $it;
2460 1         8 $it->no_duplicates(1);
2461 1 50       7 if ($opts->{isa_only}) {
2462 0         0 $it->reltype_filter("is_a");
2463             }
2464 1 50       7 if ($fmt eq "gotext") {
    0          
    0          
2465 1         4 while (my $ni = $it->next_node_instance) {
2466 5         27 my $depth = $ni->depth;
2467 5         26 my $term = $ni->term;
2468 5 50       39 next if $term->is_relationship_type;
2469 5         34 my $parent_rel = $ni->parent_rel;
2470 5         13 my $line = " " x $depth;
2471 5 100       23 my $prefix =
2472             $ni->parent_rel ? $self->_rel_prefix($ni->parent_rel) : "\$";
2473 5         36 $line .=
2474             $term->to_text(-prefix=>$prefix,
2475             -escape=>1,
2476             -suppress=>$suppress,
2477             );
2478              
2479 5         17 my $parents =
2480             $self->get_parent_relationships($term->acc);
2481 5         13 my @others = @$parents;
2482 5 100       14 if ($parent_rel) {
2483 4         6 @others = grep {$_->acc1 ne $parent_rel->acc1} @$parents;
  4         13  
2484 4 50       12 if ($disp_filter) {
2485 0         0 my %filh = ();
2486 0 0       0 $disp_filter = [$disp_filter] unless ref($disp_filter);
2487 0         0 %filh = map {lc($_)=>1} @$disp_filter;
  0         0  
2488 0         0 @others = grep { $filh{lc($_->type)} } @others;
  0         0  
2489             }
2490             }
2491 5         11 foreach my $rel (@others) {
2492 0         0 my $prefix =
2493             $self->_rel_prefix($rel);
2494 0         0 my $n =
2495             $self->get_term($rel->acc1);
2496 0 0       0 next unless $n; # not in graph horizon
2497 0         0 $line .=
2498             sprintf(" %s %s ; %s",
2499             $prefix,
2500             $n->name,
2501             $n->public_acc);
2502             }
2503 5 50       14 if ($opts->{show_counts}) {
2504 0         0 $line.= " [gps: ".$term->n_deep_products."]";
2505 0 0       0 if ($opts->{grouped_by_taxid}) {
2506 0   0     0 my $sh = $opts->{species_hash} || {};
2507 0         0 my $n_by_tax = $term->n_deep_products_grouped_by_taxid;
2508 0         0 my @taxids = sort {$n_by_tax->{$b} <=> $n_by_tax->{$a}} keys %$n_by_tax;
  0         0  
2509             # arbitrarily select first 10...
2510 0         0 my @staxids = splice(@taxids,0,10);
2511 0         0 $line .= " by_tax=";
2512 0         0 foreach (@staxids) {
2513 0         0 my $sn = $_;
2514 0         0 my $sp = $sh->{$_};
2515 0 0 0     0 if ($sp && $sp->binomial) {
2516 0         0 $sn = $sp->binomial;
2517             }
2518 0         0 $line .= " $sn:$n_by_tax->{$_}"
2519             }
2520             }
2521             }
2522 5         7 $line .= "\n";
2523 5 50 33     16 if ($show_assocs && $self->is_focus_node($term)) {
2524 0         0 my $al = $term->association_list;
2525 0         0 foreach my $a (@$al) {
2526 0         0 $line .= " " x $depth;
2527 0         0 $line.=
2528             sprintf(" * %s %s %s %s\n",
2529             $a->gene_product->symbol,
2530             $a->gene_product->full_name,
2531             $a->gene_product->acc,
2532 0         0 join("; ", map {$_->code} @{$a->evidence_list})
  0         0  
2533             ),
2534             }
2535             }
2536 5         990 print $fh "$line";
2537             }
2538             }
2539             elsif ($fmt eq 'tree') {
2540 0           while (my $ni = $it->next_node_instance) {
2541 0           my $depth = $ni->depth;
2542 0           my $term = $ni->term;
2543 0 0         my $rtype = $ni->parent_rel ? $ni->parent_rel->type : " ";
2544 0           my $line = " " x $depth;
2545 0           $line .=
2546             sprintf("[%s] ", uc(substr($rtype,0,1)));
2547 0           $line .= $term->name;
2548 0           print $fh "$line\n";
2549             }
2550             }
2551             elsif ($fmt eq "triples") {
2552 0           my @nodes = @{$self->get_all_nodes};
  0            
2553 0           my $line = "";
2554 0           while (my $term = shift @nodes) {
2555 0           my $parents =
2556             $self->get_parent_relationships($term->acc);
2557 0           foreach my $rel (@$parents) {
2558 0           my $p =
2559             $self->get_term($rel->acc1);
2560 0 0         next unless $p; # not in graph horizon
2561 0           $line .=
2562             sprintf("(\"%s\" %s \"%s\")\n",
2563             $term->name,
2564             $rel->type,
2565             $p->name);
2566             }
2567 0           print $fh "$line";
2568             }
2569             }
2570             else {
2571 0           while (my $ni = $it->next_node_instance) {
2572 0           my $term = $ni->term;
2573 0 0         next if $term->is_relationship_type;
2574 0           my $depth = $ni->depth;
2575 0           my $parent_rel = $ni->parent_rel;
2576 0 0         tab($depth, $self->is_focus_node($term) ? "->" : " ");
2577 0           my %th = qw(isa % partof < developsfrom ~);
2578 0 0 0       printf $fh
2579             "%2s Term = %s (%s) // products=%s // $depth\n",
2580             $ni->parent_rel ? $th{$ni->parent_rel->type} : "",
2581             $term->name,
2582             $term->public_acc,
2583             $term->n_deep_products || 0,
2584             $depth,
2585             ;
2586 0 0 0       if ($show_assocs && $self->is_focus_node($term)) {
2587 0           my $al = $term->association_list;
2588 0           foreach my $a (@$al) {
2589 0           tab $depth;
2590 0           printf $fh "\t* %s %s %s\n",
2591             $a->gene_product->symbol,
2592             $a->gene_product->full_name,
2593             $a->gene_product->acc,
2594 0           join("; ", map {$_->code} @{$a->evidence_list}),
  0            
2595             }
2596             }
2597             }
2598             }
2599             }
2600              
2601              
2602             #Removes a node and connects the parents directly
2603             #to the children. It is a tricky question which
2604             #rel_type to use for the new connection (the parent
2605             #and childs rel_types might be different). For
2606             #now I just use the childs, but this may need to
2607             #be revisited.
2608             sub delete_node_with_reconnect {
2609 0     0 0   my $self = shift;
2610 0           my $acc = shift;
2611              
2612             #print "
\t\t=-=-= Removing $acc
\n";
2613              
2614             # First adjust the child and parent relationships
2615 0   0       my $par_rel_hashes = $self->{parent_relationships_h}->{$acc} || {};
2616 0           my $par_rels = [grep {$_} values(%$par_rel_hashes)];
  0            
2617 0   0       my $child_rel_hashes = $self->{child_relationships_h}->{$acc} || {};
2618 0           my $child_rels = [grep {$_} values(%$child_rel_hashes)];
  0            
2619              
2620 0           my ($par_rel, $child_rel);
2621 0           foreach $par_rel (@$par_rels) {
2622 0           my $par_acc = $par_rel->acc1;
2623 0           my $par_type = $par_rel->type;
2624 0           foreach $child_rel (@$child_rels) {
2625 0           my $child_acc = $child_rel->acc2;
2626 0           my $child_type = $child_rel->type;
2627              
2628             # There's a heirarchy of types
2629 0           my $rel_type = $child_type;
2630             #qw(isa partof developsfrom);
2631              
2632             #print "
\t\t=-=-=\t\t  Adding $par_acc -> $child_acc ($rel_type)
\n";
2633 0           $self->add_relationship({acc1=>$par_acc,
2634             acc2=>$child_acc,
2635             type=>$rel_type});
2636             }
2637             }
2638              
2639             # And get rid of the node itself
2640 0           $self->delete_node($acc);
2641             }
2642              
2643              
2644             sub DEPRECATED_sub_graph {
2645 0     0 0   my ($self, $terms) = @_;
2646              
2647             # Output a clone of the graph
2648 0           my $subg = $self->clone;
2649              
2650 0           my $it = $subg->create_iterator();
2651 0           my $ni;
2652 0           while ($ni = $it->next_node_instance)
2653             {
2654 0           my $term = $ni->term;
2655 0           my $term_name = $term->name;
2656 0           my $acc = $term->public_acc;
2657 0 0         $subg->delete_node_with_reconnect($acc) unless (grep {$_->public_acc eq $term->public_acc} @$terms);
  0            
2658             #print_debug_line("Keeping term \"$term_name\" in graph") if (grep {$_->public_acc eq $term->public_acc} @$terms);
2659             }
2660              
2661 0           return $subg;
2662             }
2663              
2664             sub max_depth
2665             {
2666 0     0 0   my ($self) = @_;
2667              
2668 0           my $it = $self->create_iterator();
2669              
2670 0           my $max_d = 0;
2671 0           my $ni;
2672 0           while ($ni = $it->next_node_instance)
2673             {
2674 0           my $depth = $ni->depth;
2675 0           $max_d = max($max_d, $depth);
2676             }
2677              
2678 0           return $max_d;
2679             }
2680              
2681             sub split_graph_by_re {
2682 0     0 0   my ($acc, $re, $rtype, $orthogroot) =
2683             rearrange([qw(acc re rtype re orthogroot)], @_);
2684 0     0     my $func = sub {$_=shift->name;/$re/;print STDERR "$re on $_;xx=$1\n";($1)};
  0            
  0            
  0            
  0            
2685 0           shift->split_graph_by_func($acc,$func,$rtype,$orthogroot);
2686             }
2687              
2688             sub split_graph_by_func {
2689 0     0 0   my $self = shift;
2690 0           my ($acc, $func, $rtype, $orthogroot) =
2691             rearrange([qw(acc func rtype re orthogroot)], @_);
2692             # my $ng = ref($self)->new;
2693 0           my $ng = $self->apph->create_graph_obj;
2694              
2695 0           my $new_acc = $self->apph->new_acc;
2696 0           my $root = $self->get_term($acc);
2697             # $ng->add_term($root);
2698 0           my $it = $self->create_iterator($acc);
2699 0           my %h = ();
2700 0           while (my $ni = $it->next_node_instance) {
2701 0           my $term = $ni->term;
2702 0           my $rel = $ni->parent_rel;
2703 0 0 0       next unless !$rel || lc($rel->type) eq "is_a";
2704 0           my ($n) = &$func($term);
2705             # my $t1 = GO::Model::Term->new({name=>$n1});
2706             # my $t2 = GO::Model::Term->new({name=>$n});
2707 0           my $t2;
2708 0           $t2 = $self->apph->get_term({search=>$n});
2709 0 0         if (!$t2) {
2710 0           print STDERR "$n not found; checking graph\n";
2711 0           my $all = $ng->get_all_nodes;
2712 0           ($t2) = grep { $_->name eq $n } @$all;
  0            
2713             }
2714 0 0         if (!$t2) {
2715 0           print STDERR "$n not found; creating new\n";
2716 0           $t2 = $self->apph->create_term_obj({name=>$n});
2717 0           $t2->type("new");
2718 0           $t2->acc($new_acc++);
2719             }
2720 0           $h{$term->acc} = $t2;
2721              
2722             # original term now gets flattened in main graph
2723 0           $ng->add_term($term);
2724             # $ng->add_relationship($root->acc, $term->acc, $rel->type) if $rel;
2725 0 0         if ($rel) {
2726 0 0         $ng->add_relationship($rel->acc1, $term->acc, $rel->type) if $rel->acc1;
2727             }
2728              
2729             # this part gets externalised and the relationship
2730             # gets preserved here
2731 0           $ng->add_term($t2);
2732 0 0         if ($rel) {
2733 0           my $np = $h{$rel->acc1};
2734 0 0         if ($np) {
2735             # new externalised ontology
2736 0           $ng->add_relationship($np->acc, $t2->acc, $rel->type);
2737             # x-product
2738 0           $ng->add_relationship($t2->acc, $term->acc, $rtype);
2739             }
2740             }
2741             }
2742 0           return $ng;
2743             }
2744              
2745             sub store {
2746 0     0 0   my $self = shift;
2747 0           foreach my $t (@{$self->get_all_nodes}) {
  0            
2748 0           $self->apph->add_term($t);
2749             }
2750 0           foreach my $r (@{$self->get_all_relationships}) {
  0            
2751 0           $self->apph->add_relationship($r);
2752             }
2753             }
2754              
2755             # **** EXPERIMENTAL CODE ****
2756             # the idea is to be homogeneous and use graphs for
2757             # everything; eg gene products are nodes in a graph,
2758             # associations are arcs
2759             # cf rdf, daml+oil etc
2760             sub graphify {
2761 0     0 0   my $self = shift;
2762 0           my ($subg, $opts) =
2763             rearrange([qw(graph opts)], @_);
2764              
2765 0 0         $opts = {} unless $opts;
2766 0 0         $subg = $self unless $subg;
2767              
2768 0           foreach my $term (@{$self->get_all_nodes}) {
  0            
2769 0           $term->graphify($subg);
2770             }
2771 0           $subg;
2772             }
2773              
2774             1;