File Coverage

blib/lib/AI/FuzzyInference/Set.pm
Criterion Covered Total %
statement 6 155 3.8
branch 0 48 0.0
condition 0 12 0.0
subroutine 2 20 10.0
pod 0 17 0.0
total 8 252 3.1


line stmt bran cond sub pod time code
1              
2             # A module to implement a fuzzy term set.
3             # Only triangular term sets are allowed.
4             #
5             # Copyright Ala Qumsieh (ala_qumsieh@yahoo.com) 2002.
6             # This program is distributed under the same terms as Perl itself.
7              
8             package AI::FuzzyInference::Set;
9 1     1   11 use strict;
  1         2  
  1         40  
10              
11             #our $VERSION = 0.02;
12 1     1   5 use vars qw/$VERSION/; # a bit more backward compatibility.
  1         2  
  1         2082  
13             $VERSION = 0.04;
14              
15             1;
16              
17             sub new {
18 0     0 0   my $self = shift;
19 0   0       my $class = ref($self) || $self;
20              
21 0           my $obj = bless {} => $class;
22              
23 0           $obj->_init;
24              
25 0           return $obj;
26             }
27              
28             sub _init {
29 0     0     my $self = shift;
30              
31 0           $self->{TS} = {};
32 0           $self->{AREA} = {};
33             }
34              
35             sub add {
36 0     0 0   my ($self,
37             $name,
38             $xmin,
39             $xmax,
40             @coords,
41             ) = @_;
42              
43             # make sure coords span the whole universe.
44 0 0         if ($coords[0] > $xmin) {
45 0           unshift @coords => ($xmin, $coords[1]);
46             }
47              
48 0 0         if ($coords[-2] < $xmax) {
49 0           push @coords => ($xmax, $coords[-1]);
50             }
51              
52 0           $self->{TS}{$name} = \@coords;
53             }
54              
55             sub delete {
56 0     0 0   my ($self,
57             $name,
58             ) = @_;
59              
60 0           delete $self->{$_}{$name} for qw/TS AREA/;
61             }
62              
63             sub membership {
64 0     0 0   my ($self,
65             $name,
66             $val,
67             ) = @_;
68              
69 0 0         return undef unless $self->exists($name);
70              
71 0           my $deg = 0;
72 0           my @c = $self->coords($name);
73              
74 0           my $x1 = shift @c;
75 0           my $y1 = shift @c;
76              
77 0           while (@c) {
78 0           my $x2 = shift @c;
79 0           my $y2 = shift @c;
80              
81 0 0         next if $x1 == $x2; # hmm .. why do we have this?
82              
83 0 0 0       unless ($x1 <= $val && $val <= $x2) {
84 0           $x1 = $x2;
85 0           $y1 = $y2;
86 0           next;
87             }
88 0           $deg = $y2 - ($y2 - $y1) * ($x2 - $val) / ($x2 - $x1);
89 0           last;
90             }
91              
92 0           return $deg;
93             }
94              
95             sub listAll {
96 0     0 0   my $self = shift;
97              
98 0           return keys %{$self->{TS}};
  0            
99             }
100              
101             sub listMatching {
102 0     0 0   my ($self, $rgx) = @_;
103              
104 0           return grep /$rgx/, keys %{$self->{TS}};
  0            
105             }
106              
107             sub max { # max of two sets.
108 0     0 0   my ($self,
109             $set1,
110             $set2,
111             ) = @_;
112              
113 0           my @coords1 = $self->coords($set1);
114 0           my @coords2 = $self->coords($set2);
115              
116 0           my @newCoords;
117 0           my ($x, $y, $other);
118 0   0       while (@coords1 && @coords2) {
119 0 0         if ($coords1[0] < $coords2[0]) {
120 0           $x = shift @coords1;
121 0           $y = shift @coords1;
122 0           $other = $set2;
123             } else {
124 0           $x = shift @coords2;
125 0           $y = shift @coords2;
126 0           $other = $set1;
127             }
128 0           my $val = $self->membership($other, $x);
129 0 0         $val = $y if $y > $val;
130 0           push @newCoords => $x, $val;
131             }
132              
133 0 0         push @newCoords => @coords1 if @coords1;
134 0 0         push @newCoords => @coords2 if @coords2;
135              
136 0           return @newCoords;
137             }
138              
139             sub min { # min of two sets.
140 0     0 0   my ($self,
141             $set1,
142             $set2,
143             ) = @_;
144              
145 0           my @coords1 = $self->coords($set1);
146 0           my @coords2 = $self->coords($set2);
147              
148 0           my @newCoords;
149 0           my ($x, $y, $other);
150 0   0       while (@coords1 && @coords2) {
151 0 0         if ($coords1[0] < $coords2[0]) {
152 0           $x = shift @coords1;
153 0           $y = shift @coords1;
154 0           $other = $set2;
155             } else {
156 0           $x = shift @coords2;
157 0           $y = shift @coords2;
158 0           $other = $set1;
159             }
160 0           my $val = $self->membership($other, $x);
161 0 0         $val = $y if $y < $val;
162 0           push @newCoords => $x, $val;
163             }
164              
165 0 0         push @newCoords => @coords1 if @coords1;
166 0 0         push @newCoords => @coords2 if @coords2;
167              
168 0           return @newCoords;
169             }
170              
171             sub complement {
172 0     0 0   my ($self, $name) = @_;
173              
174 0           my @coords = $self->coords($name);
175 0           my $i = 0;
176 0 0         return map {++$i % 2 ? $_ : 1 - $_} @coords;
  0            
177             }
178              
179             sub coords {
180 0     0 0   my ($self,
181             $name,
182             ) = @_;
183              
184 0 0         return undef unless $self->exists($name);
185              
186 0           return @{$self->{TS}{$name}};
  0            
187             }
188              
189             sub scale { # product implication
190 0     0 0   my ($self,
191             $name,
192             $scale,
193             ) = @_;
194              
195 0           my $i = 0;
196 0 0         my @c = map { $_ * ++$i % 2 ? 1 : $scale } $self->coords($name);
  0            
197              
198 0           return @c;
199             }
200              
201             sub clip { # min implication
202 0     0 0   my ($self,
203             $name,
204             $val,
205             ) = @_;
206              
207 0           my $i = 0;
208 0 0         my @c = map {
    0          
209 0           ++$i % 2 ? $_ : $_ > $val ? $val : $_
210             }$self->coords($name);
211              
212 0           return @c;
213             }
214              
215             # had to roll my own centroid algorithm.
216             # not sure why standard algorithms didn't work
217             # correctly!
218             sub centroid { # center of mass.
219 0     0 0   my ($self,
220             $name,
221             ) = @_;
222              
223 0 0         return undef unless $self->exists($name);
224              
225 0           my @coords = $self->coords($name);
226 0           my @ar;
227              
228 0           my $x0 = shift @coords;
229 0           my $y0 = shift @coords;
230 0           my ($x1, $y1);
231              
232 0           while (@coords) {
233 0           $x1 = shift @coords;
234 0           $y1 = shift @coords;
235              
236 0           my $a1 = abs(0.5 * ($x1 - $x0) * ($y1 - $y0));
237 0 0         my $c1 = (1/3) * ($x0 + $x1 + ($y1 > $y0 ? $x1 : $x0));
238              
239 0 0         my $a2 = abs(($x1 - $x0) * ($y0 < $y1 ? $y0 : $y1));
240 0           my $c2 = $x0 + 0.5 * ($x1 - $x0);
241              
242 0           my $ta = $a1 + $a2;
243 0 0         next if $ta == 0;
244              
245 0           my $c = $c1 * ($a1 / $ta);
246 0           $c += $c2 * ($a2 / $ta);
247              
248 0           push @ar => [$c, $ta];
249             } continue {
250 0           $x0 = $x1;
251 0           $y0 = $y1;
252             }
253              
254 0           my $ta = 0;
255 0           $ta += $_->[1] for @ar;
256              
257 0           my $c = 0;
258 0           $c += $_->[0] * ($_->[1] / $ta) for @ar;
259              
260 0           return $c;
261             }
262              
263             sub median {
264 0     0 0   my ($self,
265             $name,
266             ) = @_;
267              
268 0           my @coords = $self->coords($name);
269              
270             # hmmm .. how do I do *this*?
271 0           return 0;
272             }
273              
274             sub exists {
275 0     0 0   my ($self,
276             $name,
277             ) = @_;
278              
279 0           return exists $self->{TS}{$name};
280             }
281              
282             sub uniquify {
283 0     0 0   my $self = shift;
284              
285 0           my @new;
286             my %seen;
287              
288 0           while (@_) {
289 0           my $x = shift;
290 0           my $y = shift;
291              
292 0 0         next if $seen{$x};
293              
294 0           push @new => ($x, $y);
295 0           $seen{$x} = 1;
296             }
297              
298 0           return @new;
299             }
300              
301             sub area {
302 0     0 0   my ($self, $name) = @_;
303              
304 0 0         return $self->{AREA}{$name} if exists $self->{AREA}{$name};
305              
306 0           my @coords = $self->coords($name);
307              
308 0           my $x0 = shift @coords;
309 0           my $y0 = shift @coords;
310 0           my $area = 0;
311              
312 0           while (@coords) {
313 0           my $x1 = shift @coords;
314 0           my $y1 = shift @coords;
315              
316 0           $area += 0.5 * ($x1 - $x0) * ($y1 + $y0);
317              
318 0           $x0 = $x1;
319 0           $y0 = $y1;
320             }
321              
322 0           return $self->{AREA}{$name} = $area;
323             }