-
Notifications
You must be signed in to change notification settings - Fork 3
/
rectangle-coverage.pl
executable file
·307 lines (222 loc) · 8.25 KB
/
rectangle-coverage.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#!/usr/bin/env perl
=head1 NAME
rectangle-coverage.pl - Find the dot maximally covered by (random) rectangles
=head1 SYNOPSIS
You might have to do
prompt% cpanm --installdeps .
first, since that module is not installed by default with L<Algorithm::Evolutionary>. Use C<sudo> if appropriate.
prompt% ./rectangle-coverage.pl <number-of-rectangles> <arena-side> <bits-per-coordinate> <population> <number of generations> <selection rate>
Or
prompt% ./rectangle-coverage.pl
And change variable values from the user interface
=head1 DESCRIPTION
A demo that combines the L<Algorithm::Evolutionary::Op::Easy> module
with L<Tk> to create a visual demo of the evolutionary
algorithm. It generates randomly a number of rectangles, and shows
how the population evolves to find the solution. The best point is
shown in darkening yellow color, the rest of the population in
green.
Use "Start" to start the algorithm after setting the variables, and
then Finish to stop the EA, Exit to close the window.
Default values are as follows
=over
=item *
I<number of rectangles>: 300
=item *
I<arena-side>: 10 This is independent from the number of pixels, set
by default to 600x600.
=item *
I<bits-per-coordinate>: 32 (this is the chromosome length divided by two;
there are two "genes")
=item *
I<population size>: 64
=item *
I<number of generations>: 200
=item *
I<selection rate>: 20% (will be replaced each generation); this means it's a steady state algorithm, which only changes a part of the population each generation.
=back
This program also demonstrates the use of caches in the fitness
evaluation, so be careful if you use too many bits or too many
generations, check out memory usage.
Console output shows the number of generations, the winning chromosome, and
fitness. After finishing, it outputs time, cache ratio and some other
things.
=cut
use Tk;
use strict;
use warnings;
use Algorithm::RectanglesContainingDot;
use lib qw(lib ../lib);
use Algorithm::Evolutionary qw( Individual::BitString Op::Easy
Op::Bitflip Op::Crossover );
my $width = 600;
my $height = 500;
# Create MainWindow and configure:
my $mw = MainWindow->new;
$mw->configure( -width=>$width, -height=>$width );
$mw->resizable( 0, 0 ); # not resizable in any direction
my $num_rects = shift || 300;
my $arena_side = shift || 10;
my $bits = shift || 32;
my $pop_size = shift || 64; #Population size
my $number_of_generations = shift || 200; #Max number of generations
my $selection_rate = shift || 0.2;
my $scale_x = $arena_side/$width;
my $scale_y = $arena_side/$height;
my $alg = Algorithm::RectanglesContainingDot->new;
my $fitness;
my $generation;
my @pop;
# Start Evolutionary Algorithm
my $contador=0;
my $dot_size = 6;
my $mini_dot_size = $dot_size/2;
my @dot_population;
# Create and configure the widgets
my $f = $mw->Frame(-relief => 'groove',
-bd => 2)->pack(-side => 'top',
-fill => 'x');
for my $v ( qw( num_rects arena_side bits pop_size number_of_generations selection_rate ) ){
create_and_pack( $f, $v );
}
my $canvas = $mw->Canvas( -cursor=>"crosshair", -background=>"white",
-width=>$width, -height=>$height )->pack;
$mw->Button( -text => 'Start',
-command => \&start,
)->pack( -side => 'left',
-expand => 1);
$mw->Button( -text => 'End',
-command => \&finished,
)->pack( -side => 'left',
-expand => 1 );
$mw->Button( -text => 'Exit',
-command => sub { exit(0);},
)->pack( -side => 'left',
-expand => 1 );
$mw->eventAdd('<<Gen>>' => '<Control-Shift-G>'); # Improbable combination
$mw->eventAdd('<<Fin>>' => '<Control-C>');
$mw->bind('<<Gen>>' => \&generation);
$mw->bind('<<Fin>>' => \&finished );
sub create_and_pack {
my $frame = shift;
my $var = shift;
my $f = $frame->Frame();
my $label = $f->Label(-text => $var )->pack(-side => 'left');
my $entry = $f->Entry( -textvariable => eval '\$'.$var )->pack(-side => 'right' );
$f->pack();
}
sub start {
#Generate random rectangles
for my $i (0 .. $num_rects) {
my $x_0 = rand( $arena_side );
my $y_0 = rand( $arena_side);
my $side_x = rand( $arena_side - $x_0 );
my $side_y = rand($arena_side-$y_0);
$alg->add_rectangle("rectangle_$i", $x_0, $y_0,
$x_0+$side_x, $x_0+$side_y );
my $val = 255*$i/$num_rects;
my $color = sprintf( "#%02x%02x%02x", $val, $val, $val );
$canvas->createRectangle( $x_0/$scale_x, $y_0/$scale_y,
$side_x/$scale_x, $side_y/$scale_y,
-outline =>$color );
}
#Declare fitness function
$fitness = sub {
my $individual = shift;
my ( $dot_x, $dot_y ) = $individual->decode($bits/2,0, $arena_side);
my @contained_in = $alg->rectangles_containing_dot($dot_x, $dot_y);
return scalar @contained_in;
};
#----------------------------------------------------------#
#Initial population
#Creamos $pop_size individuos
for ( 0..$pop_size ) {
my $indi = Algorithm::Evolutionary::Individual::BitString->new( $bits );
push( @pop, $indi );
}
#----------------------------------------------------------#
# Variation operators
my $m = Algorithm::Evolutionary::Op::Bitflip->new; # Rate = 1
my $c = Algorithm::Evolutionary::Op::Crossover->new(2, 9 ); # Rate = 9
#----------------------------------------------------------#
#Usamos estos operadores para definir una generación del algoritmo. Lo cual
# no es realmente necesario ya que este algoritmo define ambos operadores por
# defecto. Los parámetros son la función de fitness, la tasa de selección y los
# operadores de variación.
$generation = Algorithm::Evolutionary::Op::Easy->new( $fitness , $selection_rate , [$m, $c] ) ;
#----------------------------------------------------------#
for ( @pop ) {
if ( !defined $_->Fitness() ) {
my $this_fitness = $fitness->($_);
$_->Fitness( $this_fitness );
}
}
#Start the music
$mw->eventGenerate( '<<Gen>>', -when => 'tail' );
}
sub as_point {
my $individual = shift || die "Nobody here!n";
my @point = $individual->decode($bits/2,0, $arena_side);
return ($point[0]/$scale_x, $point[1]/$scale_y);
}
sub generation {
while (@dot_population) {
$canvas->delete( shift @dot_population );
}
$generation->apply( \@pop );
print "Pop size $#pop\n";
my $val = 255*$contador/$number_of_generations;
my $color = sprintf( "#%02x%02x00", 255-$val, 255-$val );
my ($point_x, $point_y) = as_point( $pop[0] );
print "$contador : ", $pop[0]->asString(), ", Color $color\n\tDecodes to $point_x, $point_y\n" ;
$contador++;
$canvas->createOval($point_x-$dot_size, $point_y-$dot_size,
$point_x+$dot_size, $point_y+$dot_size,
-fill => $color );
for my $p ( @pop ) {
my @point = as_point( $p );
push @dot_population,$canvas->createOval($point[0]-$mini_dot_size, $point[1]-$mini_dot_size,
$point[0]+$mini_dot_size, $point[1]+$mini_dot_size,
-fill => "#00ff00" );
}
$canvas->update();
if ( ($contador < $number_of_generations)
&& ($pop[0]->Fitness() < $num_rects)) {
$mw->eventGenerate( '<<Gen>>', -when => 'tail' );
} else {
$mw->eventGenerate( '<<Fin>>' );
}
}
sub finished {
#----------------------------------------------------------#
#leemos el mejor resultado
#Mostramos los resultados obtenidos
print "Best is:\n\t ",$pop[0]->asString()," Fitness: ",$pop[0]->Fitness(),"\n";
}
MainLoop;
=head1 SEE ALSO
First, you should obviously check
L<Algorithm::Evolutionary::Op::Easy>, and then these other classes.
=over 4
=item *
L<Algorithm::Evolutionary::Op::Base>.
=item *
L<Algorithm::Evolutionary::Individual::Base>.
=item *
L<Algorithm::Evolutionary::Fitness::Base>.
=back
L<Tk> is a prerrequisite for this program, as well as
L<Algorithm::RectanglesContainingDot>. Obviously,
L<Algorithm::Evolutionary> should be installed too, just in case
you got this independently.
=head1 AUTHOR
J. J. Merelo, C<jj (at) merelo.net>
=cut
=head1 Copyright
This file is released under the GPL. See the LICENSE file included in this distribution,
or go to http://www.fsf.org/licenses/gpl.txt
CVS Info: $Date: 2012/12/08 10:12:37 $
$Header: /media/Backup/Repos/opeal/opeal/Algorithm-Evolutionary/scripts/rectangle-coverage.pl,v 3.5 2012/12/08 10:12:37 jmerelo Exp $
$Author: jmerelo $
$Revision: 3.5 $
=cut