File Coverage

blib/lib/WordNet/Similarity/hso.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # WordNet::Similarity::hso.pm version 2.04
2             # (Last updated $Id: hso.pm,v 1.16 2008/03/27 06:21:17 sidz1979 Exp $)
3             #
4             # Semantic Similarity Measure package implementing the measure
5             # described by Hirst and St-Onge (1998).
6             #
7             # Copyright (c) 2005,
8             #
9             # Ted Pedersen, University of Minnesota Duluth
10             # tpederse at d.umn.edu
11             #
12             # Siddharth Patwardhan, University of Utah, Salt Lake City
13             # sidd at cs.utah.edu
14             #
15             # This program is free software; you can redistribute it and/or
16             # modify it under the terms of the GNU General Public License
17             # as published by the Free Software Foundation; either version 2
18             # of the License, or (at your option) any later version.
19             #
20             # This program is distributed in the hope that it will be useful,
21             # but WITHOUT ANY WARRANTY; without even the implied warranty of
22             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23             # GNU General Public License for more details.
24             #
25             # You should have received a copy of the GNU General Public License
26             # along with this program; if not, write to
27             #
28             # The Free Software Foundation, Inc.,
29             # 59 Temple Place - Suite 330,
30             # Boston, MA 02111-1307, USA.
31             #
32             # ------------------------------------------------------------------
33              
34             package WordNet::Similarity::hso;
35              
36             =head1 NAME
37              
38             WordNet::Similarity::hso - Perl module for computing semantic relatedness
39             of word senses using the method described by Hirst and St-Onge (1998).
40              
41             =head1 SYNOPSIS
42              
43             use WordNet::Similarity::hso;
44              
45             use WordNet::QueryData;
46              
47             my $wn = WordNet::QueryData->new();
48              
49             my $object = WordNet::Similarity::hso->new($wn);
50              
51             my $value = $object->getRelatedness("car#n#1", "bus#n#2");
52              
53             ($error, $errorString) = $object->getError();
54              
55             die "$errorString\n" if($error);
56              
57             print "car (sense 1) <-> bus (sense 2) = $value\n";
58              
59             =head1 DESCRIPTION
60              
61             This module computes the semantic relatedness of word senses according to
62             the method described by Hirst and St-Onge (1998). In their paper they
63             describe a method to identify 'lexical chains' in text. They measure the
64             semantic relatedness of words in text to identify the links of the lexical
65             chains. This measure of relatedness has been implemented in this module.
66              
67             =head2 Methods
68              
69             =over
70              
71             =cut
72              
73 3     3   6762 use strict;
  3         7  
  3         110  
74              
75 3     3   15 use Exporter;
  3         6  
  3         116  
76 3     3   1751 use WordNet::Similarity;
  0            
  0            
77              
78             use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
79              
80             @ISA = qw/WordNet::Similarity/;
81              
82             %EXPORT_TAGS = ();
83              
84             @EXPORT_OK = ();
85              
86             @EXPORT = ();
87              
88             our $VERSION = '2.04';
89              
90             =item $hso->setPosList()
91              
92             This method is internally called to determine the parts of speech
93             this measure is capable of dealing with.
94              
95             Parameters: none.
96              
97             Returns: none.
98              
99             =cut
100              
101             sub setPosList
102             {
103             my $self = shift;
104             $self->{n} = 1;
105             $self->{v} = 1;
106             $self->{a} = 1;
107             $self->{r} = 1;
108             }
109              
110             # How medium strong relations work (from an e-mail by Sid):
111             # Basically, for the
112             # medium strong relations, I had to start from the given node and explore
113             # all "legal" paths starting from that node and ending on $offset2.
114              
115             # To do this I created a recursive function _medStrong, that is called like
116             # so in line 247:
117              
118             # my $score = $self->_medStrong(0, 0, 0, $offset1, $offset1, $offset2);
119              
120             # The first parameter is the "state" (=0 initially). This parameter keeps
121             # track of what part of the path we are on. For example, one of the legal
122             # paths goes upwards, then horizontally, and then downwards. So along the
123             # upwards section of the path, "state" would be 0. Along the horizontal
124             # section of the path, the "state" becomes 1. Along the downwards section
125             # the "state" is 2. Similarly, the state recognizes different legal paths.
126              
127             # The second parameter is "distance" (=0 initially). This parameter keeps
128             # track of the length of the path that we are at. Since the maximum length
129             # possible is 5, we stop the recursive function when the path length reaches
130             # 5.
131              
132             # The third parameter is "chdir" (=0 initally) counts the number of changes
133             # in direction. This value is required in the formula for the medium-strong
134             # relation, so is updated everytime there is a change in path direction.
135              
136             # The fourth parameter is "from node" (=offset1 initally). The recursive
137             # function uses the from node to decide which are the next possible nodes in
138             # a given path. Each of these are then explored creating n recursive copies.
139              
140             # The fifth parameter is "path". It is a string of the form
141              
142             # "$offset [DUH] $offset [DUH] $offset..."
143              
144             # that stores a string representation of the path. This string is used in
145             # the traces (if the path turns out to be a legal path). A path would turn
146             # out to be a legal path if it is generated by our recursive function and
147             # the first and last nodes in the path are $offset1 and $offset2 of the
148             # relatedness measure.
149              
150             # The sixth parameter to the function is "endOffset". This is basically
151             # "$offset2" throughout and is used by the recursive function to determine
152             # if the current node in the path is the last node of that path.
153              
154             # The basic idea of the recursive function is at each stage, to determine
155             # the next possible nodes in the path, from the current node and state. Then
156             # _medStrong is called on each of these. The recursive function does a
157             # search through a tree of "partially" legal paths, until a completely legal
158             # path is found. Multiple such paths may be found. The highest of the scores
159             # of these is returned.
160              
161             # _medStrong returns the maximum length of a legal path that was found in a
162             # given subtree of the recursive search. These return values are used by
163             # _medStrong and the highest of these is returned.
164              
165              
166             =item $hso->getRelatedness ($synset1, $synset2)
167              
168             Computes the relatedness of two word senses using the method of Hirst &
169             St-Onge.
170              
171             Parameters: two word senses in "word#pos#sense" format.
172              
173             Returns: Unless a problem occurs, the return value is the relatedness
174             score, which is greater-than or equal-to 0 and less-than or equal-to 16.
175             If an error occurs, then the error level is set to non-zero and an error
176             string is created (see the description of getError()).
177              
178             =cut
179              
180             sub getRelatedness
181             {
182             my $self = shift;
183             my $class = ref $self || $self;
184             my $wps1 = shift;
185             my $wps2 = shift;
186             my $wn = $self->{wn};
187              
188             # Check the existence of the WordNet::QueryData object.
189             if(!$wn) {
190             $self->{errorString} .= "\nError (${class}::getRelatedness()) - ";
191             $self->{errorString} .= "A WordNet::QueryData object is required.";
192             $self->{error} = 2;
193             return undef;
194             }
195              
196             # Initialize traces.
197             $self->{traceString} = "";
198              
199             # Undefined input cannot go unpunished.
200             if(!$wps1 || !$wps2) {
201             $self->{errorString} .= "\nWarning (${class}::getRelatedness()) - ";
202             $self->{errorString} .= "Undefined input values.";
203             $self->{error} = ($self->{error} < 1) ? 1 : $self->{error};
204             return undef;
205             }
206              
207             # Security check -- are the input strings in the correct format (word#pos#sense).
208             my ($pos1, $pos2);
209             my ($word1, $word2);
210             if($wps1 =~ /^(\S+)\#([nvar])\#\d+$/) {
211             $word1 = $1;
212             $pos1 = $2;
213             }
214             else {
215             $self->{errorString} .= "\nWarning (${class}::getRelatedness()) - ";
216             $self->{errorString} .= "Input not in word\#pos\#sense format.";
217             $self->{error} = ($self->{error} < 1) ? 1 : $self->{error};
218             return undef;
219             }
220             if($wps2 =~ /^(\S+)\#([nvar])\#\d+$/) {
221             $word2 = $1;
222             $pos2 = $2;
223             }
224             else {
225             $self->{errorString} .= "\nWarning (${class}::getRelatedness()) - ";
226             $self->{errorString} .= "Input not in word\#pos\#sense format.";
227             $self->{error} = ($self->{error} < 1) ? 1 : $self->{error};
228             return undef;
229             }
230              
231             # Which parts of speech do we have.
232             if($pos1 !~ /[nvar]/ || $pos2 !~ /[nvar]/) {
233             $self->{errorString} .= "\nWarning (${class}::getRelatedness()) - ";
234             $self->{errorString} .= "Unknown part(s) of speech.";
235             $self->{error} = ($self->{error} < 1) ? 1 : $self->{error};
236             return 0;
237             }
238              
239             # Now check if the similarity value for these two synsets is in
240             # fact in the cache... if so return the cached value.
241             my $relatedness =
242             $self->{doCache} ? $self->fetchFromCache ($wps1, $wps2) : undef;
243             defined $relatedness and return $relatedness;
244              
245             # Now get down to really finding the relatedness of these two.
246             my $offset1 = $wn->offset($wps1);
247             my $offset2 = $wn->offset($wps2);
248              
249             if(!$offset1 || !$offset2) {
250             $self->{errorString} .= "\nWarning (${class}::getRelatedness()) - ";
251             $self->{errorString} .= "Input senses not found in WordNet.";
252             $self->{error} = ($self->{error} < 1) ? 1 : $self->{error};
253             return undef;
254             }
255              
256             $offset1 = $offset1.$pos1;
257             $offset2 = $offset2.$pos2;
258              
259             if($offset1 eq $offset2) {
260             # [trace]
261             if($self->{trace}) {
262             $self->{'traceString'} .= "Strong Rel (Synset Match) : ";
263             $self->printSet($pos1, 'offset', $offset1);
264             $self->{'traceString'} .= "\n\n";
265             }
266             # [/trace]
267              
268             $self->{doCache} and $self->storeToCache ($wps1, $wps2, 16);
269             return 16;
270             }
271              
272             my @horiz1 = &_getHorizontalOffsetsPOS($self->{wn}, $offset1);
273             my @upward1 = &_getUpwardOffsetsPOS($self->{wn}, $offset1);
274             my @downward1 = &_getDownwardOffsetsPOS($self->{wn}, $offset1);
275             my @horiz2 = &_getHorizontalOffsetsPOS($self->{wn}, $offset2);
276             my @upward2 = &_getUpwardOffsetsPOS($self->{wn}, $offset2);
277             my @downward2 = &_getDownwardOffsetsPOS($self->{wn}, $offset2);
278              
279             # [trace]
280             if($self->{trace}) {
281             $self->{traceString} .= "Horizontal Links of ";
282             $self->printSet($pos1, 'offset', $offset1);
283             $self->{traceString} .= ": ";
284             $self->printSet($pos1, 'offset', @horiz1);
285             $self->{traceString} .= "\nUpward Links of ";
286             $self->printSet($pos1, 'offset', $offset1);
287             $self->{traceString} .= ": ";
288             $self->printSet($pos1, 'offset', @upward1);
289             $self->{traceString} .= "\nDownward Links of ";
290             $self->printSet($pos1, 'offset', $offset1);
291             $self->{traceString} .= ": ";
292             $self->printSet($pos1, 'offset', @downward1);
293             $self->{traceString} .= "\nHorizontal Links of ";
294             $self->printSet($pos2, 'offset', $offset2);
295             $self->{traceString} .= ": ";
296             $self->printSet($pos2, 'offset', @horiz2);
297             $self->{traceString} .= "\nUpward Links of ";
298             $self->printSet($pos2, 'offset', $offset2);
299             $self->{traceString} .= ": ";
300             $self->printSet($pos2, 'offset', @upward2);
301             $self->{traceString} .= "\nDownward Links of ";
302             $self->printSet($pos2, 'offset', $offset2);
303             $self->{traceString} .= ": ";
304             $self->printSet($pos2, 'offset', @downward2);
305             $self->{traceString} .= "\n\n";
306             }
307             # [/trace]
308              
309             if(&_isIn($offset1, @horiz2) || &_isIn($offset2, @horiz1)) {
310             # [trace]
311             if($self->{trace}) {
312             $self->{traceString} .= "Strong Rel (Horizontal Match) : \n";
313             $self->{traceString} .= "Horizontal Links of ";
314             $self->printSet($pos1, 'offset', $offset1);
315             $self->{traceString} .= ": ";
316             $self->printSet($pos1, 'offset', @horiz1);
317             $self->{traceString} .= "\nHorizontal Links of ";
318             $self->printSet($pos2, 'offset', $offset2);
319             $self->{traceString} .= ": ";
320             $self->printSet($pos2, 'offset', @horiz2);
321             $self->{traceString} .= "\n\n";
322             }
323             # [/trace]
324              
325             $self->{doCache} and $self->storeToCache ($wps1, $wps2, 16);
326             return 16;
327             }
328              
329             if($word1 =~ /$word2/ || $word2 =~ /$word1/) {
330             if(&_isIn($offset1, @upward2) || &_isIn($offset1, @downward2)) {
331             # [trace]
332             if($self->{trace}) {
333             $self->{traceString} .= "Strong Rel (Compound Word Match) : \n";
334             $self->{traceString} .= "All Links of $word1: ";
335             $self->printSet($pos1, 'offset', @horiz1, @upward1, @downward1);
336             $self->{traceString} .= "\nAll Links of $word2: ";
337             $self->printSet($pos2, 'offset', @horiz2, @upward2, @downward2);
338             $self->{traceString} .= "\n\n";
339             }
340             # [/trace]
341              
342             $self->{doCache} and $self->storeToCache ($wps1, $wps2, 16);
343             return 16;
344             }
345              
346             if(&_isIn($offset2, @upward1) || &_isIn($offset2, @downward1)) {
347             # [trace]
348             if($self->{trace}) {
349             $self->{traceString} .= "Strong Rel (Compound Word Match) : \n";
350             $self->{traceString} .= "All Links of $word1: ";
351             $self->printSet($pos1, 'offset', @horiz1, @upward1, @downward1);
352             $self->{traceString} .= "\nAll Links of $word2: ";
353             $self->printSet($pos2, 'offset', @horiz2, @upward2, @downward2);
354             $self->{traceString} .= "\n\n";
355             }
356             # [/trace]
357              
358             $self->{doCache} and $self->storeToCache ($wps1, $wps2, 16);
359             }
360             }
361              
362             # Conditions for Medium-Strong relations ...
363             my $score = $self->_medStrong(0, 0, 0, $offset1, $offset1, $offset2);
364              
365             $self->{doCache} and $self->storeToCache ($wps1, $wps2, $score);
366             return $score;
367             }
368              
369             # Subroutine to get offsets(POS) of all horizontal links from a given
370             # word (offset(POS)). All horizontal links specified are --
371             # Also See, Antonymy, Attribute, Pertinence, Similarity.
372             # INPUT PARAMS : $wn .. WordNet::QueryData object.
373             # $offset .. An offset-pos (e.g. 637554v)
374             # RETURN VALUES : @offsets .. Array of offset-pos (e.g. 736438n)
375             sub _getHorizontalOffsetsPOS
376             {
377             my $wn;
378             my $offset;
379             my $synset;
380             my $pos;
381             my $wordForm;
382             my @partsOfSpeech;
383             my @synsets;
384             my @offsets;
385              
386             $wn = shift;
387             $offset = shift;
388             @offsets = ();
389             if($offset =~ /^([0-9]+)([a-z])$/)
390             {
391             $offset = $1;
392             $pos = $2;
393             }
394             else
395             {
396             return @offsets;
397             }
398             $wordForm = $wn->getSense($offset,$pos);
399             @synsets = $wn->queryWord($wordForm, "also");
400             push @synsets, $wn->queryWord($wordForm, "ants");
401             push @synsets, $wn->querySense($wordForm, "attr");
402             push @synsets, $wn->queryWord($wordForm, "pert");
403             push @synsets, $wn->querySense($wordForm, "sim");
404             foreach $synset (@synsets)
405             {
406             $pos = $synset;
407             if($pos =~ /.*\#([a-z])\#.*/)
408             {
409             $pos = $1;
410             push @offsets, $wn->offset($synset).$pos;
411             }
412             }
413             return @offsets;
414             }
415              
416              
417             # Subroutine that returns all offsetPOSs that are linked
418             # to a given synset by upward links. Upward link types --
419             # Hypernymy, Meronymy
420             # INPUT PARAMS : $wn .. WordNet::QueryData object.
421             # $offset .. OffsetPOS of the synset.
422             # RETURN VALUES : @offsets .. Array of offsetPOSs.
423             sub _getUpwardOffsetsPOS
424             {
425             my $wn;
426             my $offset;
427             my $synset;
428             my $pos;
429             my $wordForm;
430             my @partsOfSpeech;
431             my @synsets;
432             my @offsets;
433              
434             $wn = shift;
435             $offset = shift;
436             @offsets = ();
437             if($offset =~ /^([0-9]+)([a-z])$/)
438             {
439             $offset = $1;
440             $pos = $2;
441             }
442             else
443             {
444             return @offsets;
445             }
446             $wordForm = $wn->getSense($offset,$pos);
447             @synsets = $wn->querySense($wordForm, "hypes");
448             push @synsets, $wn->querySense($wordForm, "mero");
449             foreach $synset (@synsets)
450             {
451             $pos = $synset;
452             if($pos =~ /.*\#([a-z])\#.*/)
453             {
454             $pos = $1;
455             push @offsets, $wn->offset($synset).$pos;
456             }
457             }
458             return @offsets;
459             }
460              
461              
462             # Subroutine that returns all offsetPOSs that are linked
463             # to a given synset by downward links. Downward link types --
464             # Cause, Entailment, Holonymy, Hyponymy.
465             # INPUT PARAMS : $wn .. WordNet::QueryData object.
466             # $offset .. OffsetPOS of the synset.
467             # RETURN VALUES : @offsets .. Array of offsetPOSs.
468             sub _getDownwardOffsetsPOS
469             {
470             my $wn;
471             my $offset;
472             my $synset;
473             my $pos;
474             my $wordForm;
475             my @partsOfSpeech;
476             my @synsets;
477             my @offsets;
478              
479             $wn = shift;
480             $offset = shift;
481             @offsets = ();
482             if($offset =~ /^([0-9]+)([a-z])$/)
483             {
484             $offset = $1;
485             $pos = $2;
486             }
487             else
488             {
489             return @offsets;
490             }
491             $wordForm = $wn->getSense($offset,$pos);
492             @synsets = $wn->querySense($wordForm, "holo");
493             push @synsets, $wn->querySense($wordForm, "hypos");
494             push @synsets, $wn->querySense($wordForm, "enta");
495             push @synsets, $wn->querySense($wordForm, "caus");
496             foreach $synset (@synsets)
497             {
498             $pos = $synset;
499             if($pos =~ /.*\#([a-z])\#.*/)
500             {
501             $pos = $1;
502             push @offsets, $wn->offset($synset).$pos;
503             }
504             }
505             return @offsets;
506             }
507              
508              
509             # Subroutine that checks if an offset is in a given
510             # set of offsets.
511             # INPUT PARAMS : $offset, @offsets .. The offset and the set of
512             # offsets.
513             # RETURN VALUES : 0 or 1.
514             sub _isIn
515             {
516             my $op1;
517             my @op2;
518             my $line;
519              
520              
521             $op1 = shift;
522             @op2 = @_;
523             $line = " ".join(" ", @op2)." ";
524             if($line =~ / $op1 /) {
525             return 1;
526             }
527             return 0;
528             }
529              
530              
531             # Recursive subroutine to check the existence of a Medium-Strong
532             # relation between two synsets.
533             # INPUT PARAMS : $state, $distance, $chdir, $offset, $path, $endOffset
534             # .. The state of the state machine.
535             # Similarity (links) covered thus far.
536             # Number of changes in direction thus far.
537             # Current node.
538             # Path so far.
539             # Last offset.
540             # RETURN VALUES : $weight .. weight of the path found.
541             sub _medStrong
542             {
543             my $self;
544             my $state;
545             my $distance;
546             my $chdir;
547             my $from;
548             my $path;
549             my $endOffset;
550             my $retT;
551             my $retH;
552             my $retU;
553             my $retD;
554             my $synset;
555             my $maxVal;
556             my @horiz;
557             my @upward;
558             my @downward;
559              
560             $self = shift;
561             $state = shift;
562             $distance = shift;
563             $chdir = shift;
564             $from = shift;
565             $path = shift;
566             $endOffset = shift;
567             if($from eq $endOffset && $distance > 1)
568             {
569             # [trace]
570             if($self->{'trace'})
571             {
572             $self->{'traceString'} .= "MedStrong relation path... \n";
573             while($path =~ /([0-9]+)([nvar]?)\s*(\[[DUH]\])?\s*/g)
574             {
575             $self->printSet($2, 'offset', $1);
576             $self->{traceString} .= " $3 " if($3);
577             }
578             $self->{traceString} .= "\n";
579             }
580             # [/trace]
581             return 8 - $distance - $chdir;
582             }
583             if($distance >= 5)
584             {
585             return 0;
586             }
587             if($state == 0)
588             {
589             @horiz = &_getHorizontalOffsetsPOS($self->{'wn'}, $from);
590             @upward = &_getUpwardOffsetsPOS($self->{'wn'}, $from);
591             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
592             $retU = 0;
593             foreach $synset (@upward)
594             {
595             $retT = $self->_medStrong(1, $distance+1, 0, $synset, $path." [U] ".$synset, $endOffset);
596             $retU = $retT if($retT > $retU);
597             }
598             $retD = 0;
599             foreach $synset (@downward)
600             {
601             $retT = $self->_medStrong(2, $distance+1, 0, $synset, $path." [D] ".$synset, $endOffset);
602             $retD = $retT if($retT > $retD);
603             }
604             $retH = 0;
605             foreach $synset (@horiz)
606             {
607             $retT = $self->_medStrong(3, $distance+1, 0, $synset, $path." [H] ".$synset, $endOffset);
608             $retH = $retT if($retT > $retH);
609             }
610             return $retU if($retU > $retD && $retU > $retH);
611             return $retD if($retD > $retH);
612             return $retH;
613             }
614             if($state == 1)
615             {
616             @horiz = &_getHorizontalOffsetsPOS($self->{'wn'}, $from);
617             @upward = &_getUpwardOffsetsPOS($self->{'wn'}, $from);
618             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
619             $retU = 0;
620             foreach $synset (@upward)
621             {
622             $retT = $self->_medStrong(1, $distance+1, 0, $synset, $path." [U] ".$synset, $endOffset);
623             $retU = $retT if($retT > $retU);
624             }
625             $retD = 0;
626             foreach $synset (@downward)
627             {
628             $retT = $self->_medStrong(4, $distance+1, 1, $synset, $path." [D] ".$synset, $endOffset);
629             $retD = $retT if($retT > $retD);
630             }
631             $retH = 0;
632             foreach $synset (@horiz)
633             {
634             $retT = $self->_medStrong(5, $distance+1, 1, $synset, $path." [H] ".$synset, $endOffset);
635             $retH = $retT if($retT > $retH);
636             }
637             return $retU if($retU > $retD && $retU > $retH);
638             return $retD if($retD > $retH);
639             return $retH;
640             }
641             if($state == 2)
642             {
643             @horiz = &_getHorizontalOffsetsPOS($self->{'wn'}, $from);
644             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
645             $retD = 0;
646             foreach $synset (@downward)
647             {
648             $retT = $self->_medStrong(2, $distance+1, 0, $synset, $path." [D] ".$synset, $endOffset);
649             $retD = $retT if($retT > $retD);
650             }
651             $retH = 0;
652             foreach $synset (@horiz)
653             {
654             $retT = $self->_medStrong(6, $distance+1, 0, $synset, $path." [H] ".$synset, $endOffset);
655             $retH = $retT if($retT > $retH);
656             }
657             return ($retD > $retH) ? $retD : $retH;
658             }
659             if($state == 3)
660             {
661             @horiz = &_getHorizontalOffsetsPOS($self->{'wn'}, $from);
662             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
663             $retD = 0;
664             foreach $synset (@downward)
665             {
666             $retT = $self->_medStrong(7, $distance+1, 0, $synset, $path." [D] ".$synset, $endOffset);
667             $retD = $retT if($retT > $retD);
668             }
669             $retH = 0;
670             foreach $synset (@horiz)
671             {
672             $retT = $self->_medStrong(3, $distance+1, 0, $synset, $path." [H] ".$synset, $endOffset);
673             $retH = $retT if($retT > $retH);
674             }
675             return ($retD > $retH) ? $retD : $retH;
676             }
677             if($state == 4)
678             {
679             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
680             $retD = 0;
681             foreach $synset (@downward)
682             {
683             $retT = $self->_medStrong(4, $distance+1, 1, $synset, $path." [D] ".$synset, $endOffset);
684             $retD = $retT if($retT > $retD);
685             }
686             return $retD;
687             }
688             if($state == 5)
689             {
690             @horiz = &_getHorizontalOffsetsPOS($self->{'wn'}, $from);
691             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
692             $retD = 0;
693             foreach $synset (@downward)
694             {
695             $retT = $self->_medStrong(4, $distance+1, 2, $synset, $path." [D] ".$synset, $endOffset);
696             $retD = $retT if($retT > $retD);
697             }
698             $retH = 0;
699             foreach $synset (@horiz)
700             {
701             $retT = $self->_medStrong(5, $distance+1, 1, $synset, $path." [H] ".$synset, $endOffset);
702             $retH = $retT if($retT > $retH);
703             }
704             return ($retD > $retH) ? $retD : $retH;
705             }
706             if($state == 6)
707             {
708             @horiz = &_getHorizontalOffsetsPOS($self->{'wn'}, $from);
709             $retH = 0;
710             foreach $synset (@horiz)
711             {
712             $retT = $self->_medStrong(6, $distance+1, 1, $synset, $path." [H] ".$synset, $endOffset);
713             $retH = $retT if($retT > $retH);
714             }
715             return $retH;
716             }
717             if($state == 7)
718             {
719             @downward = &_getDownwardOffsetsPOS($self->{'wn'}, $from);
720             $retD = 0;
721             foreach $synset (@downward)
722             {
723             $retT = $self->_medStrong(7, $distance+1, 1, $synset, $path." [D] ".$synset, $endOffset);
724             $retD = $retT if($retT > $retD);
725             }
726             return $retD;
727             }
728             return 0;
729             }
730              
731             1;
732              
733             __END__