← Index
NYTProf Performance Profile   « block view • line view • sub view »
For reply.pl
  Run on Thu Oct 21 22:40:13 2010
Reported on Thu Oct 21 22:44:42 2010

Filename/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm
StatementsExecuted 300132 statements in 1.28s
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
30000111.12s132sHailo::::replyHailo::reply
300023164.0ms117msHailo::::_storageHailo::_storage (xsub)
300001146.3ms94.2msHailo::::_tokenizerHailo::_tokenizer (xsub)
300001145.8ms63.0msHailo::::_engineHailo::_engine (xsub)
3115.67ms117msHailo::::_new_classHailo::_new_class
1112.24ms20.4msHailo::::BEGIN@11Hailo::BEGIN@11
111937µs20.3msHailo::::BEGIN@15Hailo::BEGIN@15
111435µs20.9msHailo::::BEGIN@10Hailo::BEGIN@10
111259µs279µsHailo::::BEGIN@184Hailo::BEGIN@184
331166µs117msHailo::::__ANON__[:176]Hailo::__ANON__[:176]
131158µs58µsHailo::::CORE:regcompHailo::CORE:regcomp (opcode)
162142µs42µsHailo::::CORE:matchHailo::CORE:match (opcode)
11137µs107µsHailo::::BEGIN@9Hailo::BEGIN@9
11126µs3.92sHailo::::DEMOLISHHailo::DEMOLISH
31119µs19µsHailo::::CORE:sortHailo::CORE:sort (opcode)
11118µs3.92sHailo::::saveHailo::save
11118µs18µsHailo::::BEGIN@2Hailo::BEGIN@2
11115µs101µsHailo::::BEGIN@14Hailo::BEGIN@14
11114µs92µsHailo::::BEGIN@17Hailo::BEGIN@17
31111µs11µsHailo::::__ANON__[:117]Hailo::__ANON__[:117]
11111µs36µsHailo::::BEGIN@129Hailo::BEGIN@129
11111µs64µsHailo::::BEGIN@13Hailo::BEGIN@13
11111µs70µsHailo::::BEGIN@9.2Hailo::BEGIN@9.2
32110µs12µsHailo::::brainHailo::brain (xsub)
11110µs768µsHailo::::BEGIN@12Hailo::BEGIN@12
11110µs18µsHailo::::__ANON__[:71]Hailo::__ANON__[:71]
3115µs5µsHailo::::PLUGINSHailo::PLUGINS (xsub)
1115µs5µsHailo::::BEGIN@5Hailo::BEGIN@5
2114µs4µsHailo::::tokenizer_classHailo::tokenizer_class (xsub)
2113µs3µsHailo::::orderHailo::order (xsub)
1112µs2µsHailo::::engine_classHailo::engine_class (xsub)
1112µs2µsHailo::::engine_argsHailo::engine_args (xsub)
1112µs2µsHailo::::save_on_exitHailo::save_on_exit (xsub)
1112µs2µsHailo::::storage_classHailo::storage_class (xsub)
1112µs2µsHailo::::tokenizer_argsHailo::tokenizer_args (xsub)
1111µs1µsHailo::::storage_argsHailo::storage_args (xsub)
0000s0sHailo::::__ANON__[:108]Hailo::__ANON__[:108]
0000s0sHailo::::__ANON__[:151]Hailo::__ANON__[:151]
0000s0sHailo::::__ANON__[:152]Hailo::__ANON__[:152]
0000s0sHailo::::__ANON__[:157]Hailo::__ANON__[:157]
0000s0sHailo::::__ANON__[:161]Hailo::__ANON__[:161]
0000s0sHailo::::__ANON__[:188]Hailo::__ANON__[:188]
0000s0sHailo::::__ANON__[:39]Hailo::__ANON__[:39]
0000s0sHailo::::_is_interactiveHailo::_is_interactive
0000s0sHailo::::_learn_oneHailo::_learn_one
0000s0sHailo::::_train_fhHailo::_train_fh
0000s0sHailo::::learnHailo::learn
0000s0sHailo::::learn_replyHailo::learn_reply
0000s0sHailo::::statsHailo::stats
0000s0sHailo::::trainHailo::train
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package Hailo;
2
# spent 18µs within Hailo::BEGIN@2 which was called: # once (18µs+0s) by main::BEGIN@6 at line 4
BEGIN {
316µs $Hailo::AUTHORITY = 'cpan:AVAR';
4124µs118µs}
# spent 18µs making 1 call to Hailo::BEGIN@2
5
# spent 5µs within Hailo::BEGIN@5 which was called: # once (5µs+0s) by main::BEGIN@6 at line 7
BEGIN {
619µs $Hailo::VERSION = '0.57';
7116µs15µs}
# spent 5µs making 1 call to Hailo::BEGIN@5
8
9465µs3235µs
# spent 70µs (11+59) within Hailo::BEGIN@9.2 which was called: # once (11µs+59µs) by Hailo::BEGIN@9 at line 9 # spent 107µs (37+70) within Hailo::BEGIN@9 which was called: # once (37µs+70µs) by main::BEGIN@6 at line 9
use 5.010;
# spent 107µs making 1 call to Hailo::BEGIN@9 # spent 70µs making 1 call to Hailo::BEGIN@9.2 # spent 59µs making 1 call to feature::import
102165µs220.9ms
# spent 20.9ms (435µs+20.4) within Hailo::BEGIN@10 which was called: # once (435µs+20.4ms) by main::BEGIN@6 at line 10
use autodie qw(open close);
# spent 20.9ms making 1 call to Hailo::BEGIN@10 # spent 7µs making 1 call to autodie::import
112216µs221.1ms
# spent 20.4ms (2.24+18.1) within Hailo::BEGIN@11 which was called: # once (2.24ms+18.1ms) by main::BEGIN@6 at line 11
use Any::Moose;
# spent 20.4ms making 1 call to Hailo::BEGIN@11 # spent 725µs making 1 call to Any::Moose::import
12228µs21.53ms
# spent 768µs (10+759) within Hailo::BEGIN@12 which was called: # once (10µs+759µs) by main::BEGIN@6 at line 12
use Any::Moose 'X::StrictConstructor';
# spent 768µs making 1 call to Hailo::BEGIN@12 # spent 759µs making 1 call to Any::Moose::import
13228µs2116µs
# spent 64µs (11+53) within Hailo::BEGIN@13 which was called: # once (11µs+53µs) by main::BEGIN@6 at line 13
use Scalar::Util qw(blessed);
# spent 64µs making 1 call to Hailo::BEGIN@13 # spent 53µs making 1 call to Exporter::import
14233µs2186µs
# spent 101µs (15+85) within Hailo::BEGIN@14 which was called: # once (15µs+85µs) by main::BEGIN@6 at line 14
use List::Util qw(first);
# spent 101µs making 1 call to Hailo::BEGIN@14 # spent 85µs making 1 call to Exporter::import
152169µs221.8ms
# spent 20.3ms (937µs+19.4) within Hailo::BEGIN@15 which was called: # once (937µs+19.4ms) by main::BEGIN@6 at line 15
use namespace::clean -except => 'meta';
# spent 20.3ms making 1 call to Hailo::BEGIN@15 # spent 1.55ms making 1 call to namespace::clean::import
16
17110µs178µs
# spent 92µs (14+78) within Hailo::BEGIN@17 which was called: # once (14µs+78µs) by main::BEGIN@6 at line 25
use constant PLUGINS => [ qw[
# spent 78µs making 1 call to constant::import
18 Hailo::Engine::Default
19 Hailo::Storage::MySQL
20 Hailo::Storage::PostgreSQL
21 Hailo::Storage::SQLite
22 Hailo::Tokenizer::Chars
23 Hailo::Tokenizer::Words
24 Hailo::UI::ReadLine
251342µs192µs] ];
# spent 92µs making 1 call to Hailo::BEGIN@17
26
2715µs1431µshas brain => (
# spent 431µs making 1 call to Mouse::has
28 isa => 'Str',
29 is => 'rw',
30);
31
32has order => (
33 isa => 'Int',
34 is => 'rw',
35 default => 2,
36 trigger => sub {
37 my ($self, $order) = @_;
38 $self->_custom_order(1);
39 },
4016µs1276µs);
# spent 276µs making 1 call to Mouse::has
41
4214µs1262µshas _custom_order => (
# spent 262µs making 1 call to Mouse::has
43 isa => 'Bool',
44 is => 'rw',
45 default => 0,
46 init_arg => undef,
47 documentation => "Here so we can differentiate between the default value of order being explictly set and being set by default",
48);
49
5014µs1286µshas _custom_tokenizer_class => (
# spent 286µs making 1 call to Mouse::has
51 isa => 'Bool',
52 is => 'rw',
53 default => 0,
54 init_arg => undef,
55 documentation => "Here so we can differentiate between the default value of tokenizer_class being explictly set and being set by default",
56);
57
5814µs1262µshas save_on_exit => (
# spent 262µs making 1 call to Mouse::has
59 isa => 'Bool',
60 is => 'rw',
61 default => 1,
62);
63
64has brain_resource => (
65 documentation => "Alias for `brain' for backwards compatibility",
66 isa => 'Str',
67 is => 'rw',
68
# spent 18µs (10+8) within Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:71] which was called: # once (10µs+8µs) by Mouse::Object::new at line 9 of reply.pl
trigger => sub {
6912µs my ($self, $brain) = @_;
70118µs210µs $self->brain($brain);
# spent 8µs making 1 call to Hailo::brain # spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint
71 },
7215µs1266µs);
# spent 266µs making 1 call to Mouse::has
73
7415µsmy %has = (
75 engine => {
76 name => 'Engine',
77 default => 'Default',
78 },
79 storage => {
80 name => 'Storage',
81 default => 'SQLite',
82 },
83 tokenizer => {
84 name => 'Tokenizer',
85 default => 'Words',
86 },
87 ui => {
88 name => 'UI',
89 default => 'ReadLine',
90 },
91);
92
9313µsfor my $k (keys %has) {
9447µs my $name = $has{$k}->{name};
9545µs my $default = $has{$k}->{default};
9645µs my $method_class = "${k}_class";
9744µs my $method_args = "${k}_args";
98
99 # working classes
100 has "${k}_class" => (
101 isa => 'Str',
102 is => "rw",
103 default => $default,
104 ($k ~~ 'tokenizer'
105 ? (trigger => sub {
106 my ($self, $class) = @_;
107 $self->_custom_tokenizer_class(1);
108 })
109423µs41.07ms : ())
# spent 1.07ms making 4 calls to Mouse::has, avg 268µs/call
110 );
111
112 # Object arguments
113 has "${k}_args" => (
114 documentation => "Arguments for the $name class",
115 isa => 'HashRef',
116 is => "ro",
117315µs
# spent 11µs within Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:117] which was called 3 times, avg 4µs/call: # 3 times (11µs+0s) by Mouse::Object::new at line 9 of reply.pl, avg 4µs/call
default => sub { +{} },
118428µs41.08ms );
# spent 1.08ms making 4 calls to Mouse::has, avg 269µs/call
119
120 # Working objects
121416µs42.12ms has "_${k}" => (
# spent 2.12ms making 4 calls to Mouse::has, avg 531µs/call
122 does => "Hailo::Role::$name",
123 lazy_build => 1,
124 is => 'ro',
125 init_arg => undef,
126 );
127
128 # Generate the object itself
1292333µs260µs
# spent 36µs (11+25) within Hailo::BEGIN@129 which was called: # once (11µs+25µs) by main::BEGIN@6 at line 129
no strict 'refs';
# spent 36µs making 1 call to Hailo::BEGIN@129 # spent 25µs making 1 call to strict::unimport
130
# spent 117ms (166µs+117) within Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] which was called 3 times, avg 39.1ms/call: # once (84µs+52.5ms) by Hailo::_storage at line 309 # once (30µs+47.8ms) by Hailo::_tokenizer at line 317 # once (53µs+17.0ms) by Hailo::_engine at line 316
*{"_build__${k}"} = sub {
13136µs my ($self) = @_;
132
133 my $obj = $self->_new_class(
134 $name,
135 $self->$method_class,
136 {
137 arguments => $self->$method_args,
138 ($k ~~ [ qw< engine storage > ]
139 ? (order => $self->order)
140 : ()),
141 ($k ~~ [ qw< engine > ]
142 ? (storage => $self->_storage)
143 : ()),
144 (($k ~~ [ qw< storage > ] and defined $self->brain)
145 ? (
1463143µs15117ms hailo => do {
# spent 117ms making 3 calls to Hailo::_new_class, avg 39.1ms/call # spent 4µs making 2 calls to Hailo::tokenizer_class, avg 2µs/call # spent 3µs making 2 calls to Hailo::brain, avg 2µs/call # spent 3µs making 2 calls to Hailo::order, avg 2µs/call # spent 2µs making 1 call to Hailo::engine_class # spent 2µs making 1 call to Hailo::_storage # spent 2µs making 1 call to Hailo::engine_args # spent 2µs making 1 call to Hailo::storage_class # spent 2µs making 1 call to Hailo::tokenizer_args # spent 1µs making 1 call to Hailo::storage_args
14712µs require Scalar::Util;
14818µs13µs Scalar::Util::weaken(my $s = $self);
# spent 3µs making 1 call to Scalar::Util::weaken
149
150 my %callback = (
151 has_custom_order => sub { $s->_custom_order },
152 has_custom_tokenizer_class => sub { $s->_custom_tokenizer_class },
153 set_order => sub {
154 my ($db_order) = @_;
155 $s->order($db_order);
156 $s->_engine->order($db_order);
157 },
158 set_tokenizer_class => sub {
159 my ($db_tokenizer_class) = @_;
160 $s->tokenizer_class($db_tokenizer_class);
161 },
162112µs );
163
16412µs \%callback;
165 },
166 brain => $self->brain
167 )
168 : ()),
169 (($k ~~ [ qw< storage > ]
170 ? (tokenizer_class => $self->tokenizer_class)
171 : ()))
172 },
173 );
174
175317µs return $obj;
176434µs };
177}
178
179
# spent 117ms (5.67+112) within Hailo::_new_class which was called 3 times, avg 39.1ms/call: # 3 times (5.67ms+112ms) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 39.1ms/call
sub _new_class {
18037µs my ($self, $type, $class, $args) = @_;
181
18232µs my $pkg;
183329µs310µs if ($class =~ m[^\+(?<custom_plugin>.+)$]) {
# spent 10µs making 3 calls to Hailo::CORE:match, avg 3µs/call
18421.18ms1279µs
# spent 279µs (259+19) within Hailo::BEGIN@184 which was called: # once (259µs+19µs) by main::BEGIN@6 at line 184
$pkg = $+{custom_plugin};
# spent 279µs making 1 call to Hailo::BEGIN@184
185 } else {
186335µs35µs my @plugins = @{ $self->PLUGINS };
# spent 5µs making 3 calls to Hailo::PLUGINS, avg 2µs/call
187 # Be fuzzy about includes, e.g. DBD::SQLite or SQLite or sqlite will go
18813191µs2690µs $pkg = first { / $type : .* : $class /ix }
# spent 58µs making 13 calls to Hailo::CORE:regcomp, avg 4µs/call # spent 32µs making 13 calls to Hailo::CORE:match, avg 2µs/call
189354µs6204µs sort { length $a <=> length $b }
# spent 185µs making 3 calls to List::Util::first, avg 62µs/call # spent 19µs making 3 calls to Hailo::CORE:sort, avg 6µs/call
190 @plugins;
191
19236µs unless ($pkg) {
193 local $" = ', ';
194 my @p = grep { /$type/ } @plugins;
195 die "Couldn't find a class name matching '$class' in plugins '@p'";
196 }
197 }
198
199328µs315µs if (Any::Moose::moose_is_preferred()) {
# spent 15µs making 3 calls to Any::Moose::moose_is_preferred, avg 5µs/call
200 require Class::MOP;
201 eval { Class::MOP::load_class($pkg) };
202 } else {
2033151µs eval qq[require $pkg];
# spent 242µs executing statements in string eval # spent 164µs executing statements in string eval # spent 152µs executing statements in string eval
204 }
20534µs die $@ if $@;
206
2073230µs301.05ms return $pkg->new(%$args);
# spent 614µs making 3 calls to Mouse::Object::new, avg 205µs/call # spent 296µs making 3 calls to Mouse::Meta::Class::_calculate_all_attributes, avg 99µs/call # spent 79µs making 1 call to Hailo::Engine::Default::BUILD # spent 26µs making 1 call to Hailo::Role::Tokenizer::BUILD # spent 15µs making 10 calls to Mouse::Meta::TypeConstraint::_compiled_type_constraint, avg 1µs/call # spent 7µs making 3 calls to Mouse::Meta::Class::strict_constructor, avg 2µs/call # spent 5µs making 1 call to Hailo::Role::Tokenizer::__ANON__[Hailo/Role/Tokenizer.pm:21] # spent 5µs making 3 calls to Mouse::Meta::Class::is_immutable, avg 2µs/call # spent 4µs making 3 calls to Mouse::Meta::Class::is_anon_class, avg 1µs/call # spent 3µs making 2 calls to Mouse::Meta::Attribute::default, avg 1µs/call
208}
209
210
# spent 3.92s (18µs+3.92) within Hailo::save which was called: # once (18µs+3.92s) by Hailo::DEMOLISH at line 340
sub save {
21112µs my ($self, @args) = @_;
212110µs23.92s $self->_storage->save(@args);
# spent 3.92s making 1 call to Hailo::Storage::SQLite::save # spent 2µs making 1 call to Hailo::_storage
21317µs return;
214}
215
216sub train {
217 my ($self, $input) = @_;
218
219 $self->_storage->start_training();
220
221 given ($input) {
222 # With STDIN
223 when (not ref and defined and $_ eq '-') {
224 die "You must provide STDIN when training from '-'" if $self->_is_interactive(*STDIN);
225 $self->_train_fh(*STDIN);
226 }
227 # With a filehandle
228 when (ref eq 'GLOB') {
229 $self->_train_fh($input);
230 }
231 # With a file
232 when (not ref) {
233 open my $fh, '<:encoding(utf8)', $input;
234 $self->_train_fh($fh, $input);
235 }
236 # With an Array
237 when (ref eq 'ARRAY') {
238 $self->_learn_one($_) for @$input;
239 }
240 # With something naughty
241 default {
242 die "Unknown input: $input";
243 }
244 }
245
246 $self->_storage->stop_training();
247
248 return;
249}
250
251sub _train_fh {
252 my ($self, $fh, $filename) = @_;
253
254 while (my $line = <$fh>) {
255 chomp $line;
256 $self->_learn_one($line);
257 }
258
259 return;
260}
261
262sub learn {
263 my ($self, $input) = @_;
264 my $inputs;
265
266 given ($input) {
267 when (not defined) {
268 die "Cannot learn from undef input";
269 }
270 when (not ref) {
271 $inputs = [$input];
272 }
273 # With an Array
274 when (ref eq 'ARRAY') {
275 $inputs = $input
276 }
277 default {
278 die "Unknown input: $input";
279 }
280 }
281
282 my $storage = $self->_storage;
283
284 $storage->start_learning();
285 $self->_learn_one($_) for @$inputs;
286 $storage->stop_learning();
287 return;
288}
289
290sub _learn_one {
291 my ($self, $input) = @_;
292 my $engine = $self->_engine;
293
294 my $tokens = $self->_tokenizer->make_tokens($input);
295 $engine->learn($tokens);
296
297 return;
298}
299
300sub learn_reply {
301 my ($self, $input) = @_;
302 $self->learn($input);
303 return $self->reply($input);
304}
305
306
# spent 132s (1.12+131) within Hailo::reply which was called 30000 times, avg 4.40ms/call: # 30000 times (1.12s+131s) by main::RUNTIME at line 17 of reply.pl, avg 4.40ms/call
sub reply {
3073000048.9ms my ($self, $input) = @_;
308
30930000183ms30004169ms my $storage = $self->_storage;
# spent 117ms making 30000 calls to Hailo::_storage, avg 4µs/call # spent 52.6ms making 1 call to Hailo::__ANON__[Hailo.pm:176] # spent 117µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217] # spent 2µs making 1 call to Mouse::Meta::Attribute::builder # spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint
310 # start_training() hasn't been called so we can't guarentee that
311 # the storage has been engaged at this point. This must be called
312 # before ->_engine() is called anywhere to ensure that the
313 # lazy-loading in the engine works.
31430000160ms30001441ms $storage->_engage() unless $storage->_engaged;
# spent 389ms making 1 call to Hailo::Storage::SQLite::_engage # spent 52.0ms making 30000 calls to Hailo::Storage::_engaged, avg 2µs/call
315
31630000157ms3000480.2ms my $engine = $self->_engine;
# spent 63.0ms making 30000 calls to Hailo::_engine, avg 2µs/call # spent 17.1ms making 1 call to Hailo::__ANON__[Hailo.pm:176] # spent 115µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217] # spent 2µs making 1 call to Mouse::Meta::Attribute::builder # spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint
31730000153ms30004142ms my $tokenizer = $self->_tokenizer;
# spent 94.2ms making 30000 calls to Hailo::_tokenizer, avg 3µs/call # spent 47.8ms making 1 call to Hailo::__ANON__[Hailo.pm:176] # spent 115µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217] # spent 2µs making 1 call to Mouse::Meta::Attribute::builder # spent 1µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint
318
3193000025.9ms my $reply;
3203000075.6ms if (defined $input) {
321 my $tokens = $tokenizer->make_tokens($input);
322 $reply = $engine->reply($tokens);
323 }
324 else {
32530000127ms30000120s $reply = $engine->reply();
# spent 120s making 30000 calls to Hailo::Engine::Default::reply, avg 4.00ms/call
326 }
327
3283000034.1ms return unless defined $reply;
32930000315ms3000010.4s return $tokenizer->make_output($reply);
# spent 10.4s making 30000 calls to Hailo::Tokenizer::Words::make_output, avg 346µs/call
330}
331
332sub stats {
333 my ($self) = @_;
334
335 return $self->_storage->totals();
336}
337
338
# spent 3.92s (26µs+3.92) within Hailo::DEMOLISH which was called: # once (26µs+3.92s) by Mouse::Object::DESTROY at line 0 of reply.pl
sub DEMOLISH {
33912µs my ($self) = @_;
340121µs33.92s $self->save() if blessed $self->{_storage} and $self->save_on_exit;
# spent 3.92s making 1 call to Hailo::save # spent 3µs making 1 call to Scalar::Util::blessed # spent 2µs making 1 call to Hailo::save_on_exit
341116µs return;
342}
343
344sub _is_interactive {
345 require IO::Interactive;
346 return IO::Interactive::is_interactive();
347}
348
349132µs2157µs__PACKAGE__->meta->make_immutable;
# spent 142µs making 1 call to Mouse::Meta::Class::make_immutable # spent 14µs making 1 call to Hailo::meta
350
351=encoding utf8
352
353=head1 NAME
354
355Hailo - A pluggable Markov engine analogous to MegaHAL
356
357=head1 SYNOPSIS
358
359This is the synopsis for using Hailo as a module. See L<hailo> for
360command-line invocation.
361
362 # Hailo requires Perl 5.10
363 use 5.010;
364 use Any::Moose;
365 use Hailo;
366
367 # Construct a new in-memory Hailo using the SQLite backend. See
368 # backend documentation for other options.
369 my $hailo = Hailo->new;
370
371 # Various ways to learn
372 my @train_this = ("I like big butts", "and I can not lie");
373 $hailo->learn(\@train_this);
374 $hailo->learn($_) for @train_this;
375
376 # Heavy-duty training interface. Backends may drop some safety
377 # features like journals or synchronous IO to train faster using
378 # this mode.
379 $hailo->train("megahal.trn");
380 $hailo->train($filehandle);
381
382 # Make the brain babble
383 say $hailo->reply("hello good sir.");
384 # Just say something at random
385 say $hailo->reply();
386
387=head1 DESCRIPTION
388
389Hailo is a fast and lightweight markov engine intended to replace
390L<AI::MegaHAL|AI::MegaHAL>. It has a L<Mouse|Mouse> (or
391L<Moose|Moose>) based core with pluggable
392L<storage|Hailo::Role::Storage>, L<tokenizer|Hailo::Role::Tokenizer>
393and L<engine|Hailo::Role::Engine> backends.
394
395It is similar to MegaHAL in functionality, the main differences (with the
396default backends) being better scalability, drastically less memory usage,
397an improved tokenizer, and tidier output.
398
399With this distribution, you can create, modify, and query Hailo brains. To
400use Hailo in event-driven POE applications, you can use the
401L<POE::Component::Hailo|POE::Component::Hailo> wrapper. One example is
402L<POE::Component::IRC::Plugin::Hailo|POE::Component::IRC::Plugin::Hailo>,
403which implements an IRC chat bot.
404
405=head2 Etymology
406
407I<Hailo> is a portmanteau of I<HAL> (as in MegaHAL) and
408L<failo|http://identi.ca/failo>.
409
410=head1 Backends
411
412Hailo supports pluggable L<storage|Hailo::Role::Storage> and
413L<tokenizer|Hailo::Role::Tokenizer> backends, it also supports a
414pluggable L<UI|Hailo::Role::UI> backend which is used by the L<hailo>
415command-line utility.
416
417=head2 Storage
418
419Hailo can currently store its data in either a
420L<SQLite|Hailo::Storage::SQLite>,
421L<PostgreSQL|Hailo::Storage::PostgreSQL> or
422L<MySQL|Hailo::Storage::MySQL> database. Some NoSQL backends were
423supported in earlier versions, but they were removed as they had no
424redeeming quality.
425
426SQLite is the primary target for Hailo. It's much faster and uses less
427resources than the other two. It's highly recommended that you use it.
428
429See L<Hailo::Storage/"Comparison of backends"> for benchmarks showing
430how the various backends compare under different workloads, and how
431you can create your own.
432
433=head2 Tokenizer
434
435By default Hailo will use L<the word
436tokenizer|Hailo::Tokenizer::Words> to split up input by whitespace,
437taking into account things like quotes, sentence terminators and more.
438
439There's also a L<the character
440tokenizer|Hailo::Tokenizer::Chars>. It's not generally useful for a
441conversation bot but can be used to e.g. generate new words given a
442list of existing words.
443
444=head1 UPGRADING
445
446Hailo makes no promises about brains generated with earlier versions
447being compatable with future version and due to the way Hailo works
448there's no practical way to make that promise. Learning in Hailo is
449lossy so an accurate conversion is impossible.
450
451If you're maintaining a Hailo brain that you want to keep using you
452should save the input you trained it on and re-train when you upgrade.
453
454Hailo is always going to lose information present in the input you
455give it. How input tokens get split up and saved to the storage
456backend depends on the version of the tokenizer being used and how
457that input gets saved to the database.
458
459For instance if an earlier version of Hailo tokenized C<"foo+bar">
460simply as C<"foo+bar"> but a later version split that up into
461C<"foo", "+", "bar">, then an input of C<"foo+bar are my favorite
462metasyntactic variables"> wouldn't take into account the existing
463C<"foo+bar"> string in the database.
464
465Tokenizer changes like this would cause the brains to accumulate
466garbage and would leave other parts in a state they wouldn't otherwise
467have gotten into.
468
469There have been more drastic changes to the database format itself in
470the past.
471
472Having said all that the database format and the tokenizer are
473relatively stable. At the time of writing 0.33 is the latest release
474and it's compatable with brains down to at least 0.17. If you're
475upgrading and there isn't a big notice about the storage format being
476incompatable in the F<Changes> file your old brains will probably work
477just fine.
478
479=head1 ATTRIBUTES
480
481=head2 C<brain>
482
483The name of the brain (file name, database name) to use as storage.
484There is no default. Whether this gets used at all depends on the
485storage backend, currently only SQLite uses it.
486
487=head2 C<save_on_exit>
488
489A boolean value indicating whether Hailo should save its state before
490its object gets destroyed. This defaults to true and will simply call
491L<save|/save> at C<DEMOLISH> time.
492
493See L<Hailo::Storage::SQLite/"in_memory"> for how the SQLite backend
494uses this option in conjunction with its C<in_memory> option.
495
496=head2 C<order>
497
498The Markov order (chain length) you want to use for an empty brain.
499The default is 2.
500
501=head2 C<engine_class>
502
503=head2 C<storage_class>
504
505=head2 C<tokenizer_class>
506
507=head2 C<ui_class>
508
509A a short name name of the class we use for the engine, storage,
510tokenizer or ui backends.
511
512By default this is B<Default> for the engine, B<SQLite> for storage,
513B<Words> for the tokenizer and B<ReadLine> for the UI. The UI backend
514is only used by the L<hailo> command-line interface.
515
516You can only specify the short name of one of the packages Hailo
517itself ships with. If you need another class then just prefix the
518package with a plus (Catalyst style), e.g. C<+My::Foreign::Tokenizer>.
519
520=head2 C<engine_args>
521
522=head2 C<storage_args>
523
524=head2 C<tokenizer_args>
525
526=head2 C<ui_args>
527
528A C<HashRef> of arguments for engine/storage/tokenizer/ui
529backends. See the documentation for the backends for what sort of
530arguments they accept.
531
532=head1 METHODS
533
534=head2 C<new>
535
536This is the constructor. It accepts the attributes specified in
537L</ATTRIBUTES>.
538
539=head2 C<learn>
540
541Takes a string or an array reference of strings and learns from them.
542
543=head2 C<train>
544
545Takes a filename, filehandle or array reference and learns from all its
546lines. If a filename is passed, the file is assumed to be UTF-8 encoded.
547Unlike L<C<learn>|/learn>, this method sacrifices some safety (disables
548the database journal, fsyncs, etc) for speed while learning.
549
550=head2 C<reply>
551
552Takes an optional line of text and generates a reply that might be relevant.
553
554=head2 C<learn_reply>
555
556Takes a string argument, learns from it, and generates a reply that
557might be relevant. This is equivalent to calling L<learn|/learn>
558followed by L<reply|/reply>.
559
560=head2 C<save>
561
562Tells the underlying storage backend to L<save its
563state|Hailo::Role::Storage/"save">, any arguments to this method will
564be passed as-is to the backend.
565
566=head2 C<stats>
567
568Takes no arguments. Returns the number of tokens, expressions, previous
569token links and next token links.
570
571=head1 SUPPORT
572
573You can join the IRC channel I<#hailo> on FreeNode if you have questions.
574
575=head1 BUGS
576
577Bugs, feature requests and other issues are tracked in L<Hailo's RT on
578rt.cpan.org|https://rt.cpan.org/Dist/Display.html?Name=Hailo>
579
580=head1 SEE ALSO
581
582=over
583
584=item * L<POE::Component::Hailo> - A non-blocking POE wrapper around Hailo
585
586=item * L<POE::Component::IRC::Plugin::Hailo> - A Hailo IRC bot plugin
587
588=item * L<http://github.com/hinrik/failo> - Failo, an IRC bot that uses Hailo
589
590=item * L<http://github.com/bingos/gumbybrain> - GumbyBRAIN, a more famous IRC bot that uses Hailo
591
592=item * L<Hailo::UI::Web> - A L<Catalyst> and jQuery powered web
593interface to Hailo available at L<hailo.nix.is|http://hailo.nix.is>
594and as L<hailo-ui-web|http://github.com/avar/hailo-ui-web> on
595L<GitHub|http://github.com>
596
597=item * L<HALBot> - Another L<Catalyst> Dojo powered web interface to
598Hailo available at L<bifurcat.es|http://bifurcat.es/> and as
599L<halbot-on-the-web|http://gitorious.org/halbot-on-the-web/halbot-on-the-web>
600at L<gitorious|http://gitorious.org>
601
602=item * L<http://github.com/pteichman/cobe> - cobe, a Python port of MegaHAL "inspired by the success of Hailo"
603
604=back
605
606=head1 LINKS
607
608=over
609
610=item * L<hailo.org|http://hailo.org> - Hailo's website
611
612=item * L<http://bit.ly/hailo_rewrite_of_megahal> - Hailo: A Perl rewrite of
613MegaHAL, A blog posting about the motivation behind Hailo
614
615=item * L<http://blogs.perl.org/users/aevar_arnfjor_bjarmason/hailo/> -
616More blog posts about Hailo on E<AElig>var ArnfjE<ouml>rE<eth>
617Bjarmason's L<blogs.perl.org|http://blogs.perl.org> blog
618
619=item * Hailo on L<freshmeat|http://freshmeat.net/projects/hailo> and
620L<ohloh|https://www.ohloh.net/p/hailo>
621
622=back
623
624=head1 AUTHORS
625
626Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com
627
628E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
629
630=head1 LICENSE AND COPYRIGHT
631
632Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson and
633E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
634
635This program is free software, you can redistribute it and/or modify
636it under the same terms as Perl itself.
637
638121µs14.66ms=cut
 
# spent 42µs within Hailo::CORE:match which was called 16 times, avg 3µs/call: # 13 times (32µs+0s) by List::Util::first at line 188, avg 2µs/call # 3 times (10µs+0s) by Hailo::_new_class at line 183, avg 3µs/call
sub Hailo::CORE:match; # opcode
# spent 58µs within Hailo::CORE:regcomp which was called 13 times, avg 4µs/call: # 13 times (58µs+0s) by List::Util::first at line 188, avg 4µs/call
sub Hailo::CORE:regcomp; # opcode
# spent 19µs within Hailo::CORE:sort which was called 3 times, avg 6µs/call: # 3 times (19µs+0s) by Hailo::_new_class at line 189, avg 6µs/call
sub Hailo::CORE:sort; # opcode
# spent 5µs within Hailo::PLUGINS which was called 3 times, avg 2µs/call: # 3 times (5µs+0s) by Hailo::_new_class at line 186, avg 2µs/call
sub Hailo::PLUGINS; # xsub
# spent 63.0ms (45.8+17.2) within Hailo::_engine which was called 30000 times, avg 2µs/call: # 30000 times (45.8ms+17.2ms) by Hailo::reply at line 316, avg 2µs/call
sub Hailo::_engine; # xsub
# spent 117ms (64.0+52.7) within Hailo::_storage which was called 30002 times, avg 4µs/call: # 30000 times (64.0ms+52.7ms) by Hailo::reply at line 309, avg 4µs/call # once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146 # once (2µs+0s) by Hailo::save at line 212
sub Hailo::_storage; # xsub
# spent 94.2ms (46.3+47.9) within Hailo::_tokenizer which was called 30000 times, avg 3µs/call: # 30000 times (46.3ms+47.9ms) by Hailo::reply at line 317, avg 3µs/call
sub Hailo::_tokenizer; # xsub
# spent 12µs (10+2) within Hailo::brain which was called 3 times, avg 4µs/call: # 2 times (3µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 2µs/call # once (7µs+2µs) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:71] at line 70
sub Hailo::brain; # xsub
# spent 2µs within Hailo::engine_args which was called: # once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146
sub Hailo::engine_args; # xsub
# spent 2µs within Hailo::engine_class which was called: # once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146
sub Hailo::engine_class; # xsub
# spent 3µs within Hailo::order which was called 2 times, avg 2µs/call: # 2 times (3µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 2µs/call
sub Hailo::order; # xsub
# spent 2µs within Hailo::save_on_exit which was called: # once (2µs+0s) by Hailo::DEMOLISH at line 340
sub Hailo::save_on_exit; # xsub
# spent 1µs within Hailo::storage_args which was called: # once (1µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146
sub Hailo::storage_args; # xsub
# spent 2µs within Hailo::storage_class which was called: # once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146
sub Hailo::storage_class; # xsub
# spent 2µs within Hailo::tokenizer_args which was called: # once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146
sub Hailo::tokenizer_args; # xsub
# spent 4µs within Hailo::tokenizer_class which was called 2 times, avg 2µs/call: # 2 times (4µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 2µs/call
sub Hailo::tokenizer_class; # xsub