AI-CBR
view release on metacpan or search on metacpan
lib/AI/CBR/Case.pm view on Meta::CPAN
=item * B<param>: the parameter for the similarity function, if required
=item * B<weight>: the weight of the attribute in the comparison of the case. If you do not give a weight value for an attribute, the package's C<$DEFAULT_WEIGHT> will be used, which is 1 by default.
=item * B<value>: the value of the attribute, if you want to specify the complete case immediately. You can also do this later.
=back
=cut
sub new {
my ($class, %attributes) = @_;
# set default weights if unspecified
foreach (keys %attributes) {
$attributes{$_}->{weight} = $DEFAULT_WEIGHT unless defined $attributes{$_}->{weight};
}
my $self = \%attributes;
bless $self, $class;
return $self;
lib/AI/CBR/Case.pm view on Meta::CPAN
=head2 set_values
Pass a hash of attribute keys and values.
This will overwrite existing values, and can thus be used as a faster method
for generating new cases with the same specification.
=cut
sub set_values {
my ($self, %values) = @_;
foreach (keys %values) {
$self->{$_}->{value} = $values{$_};
}
}
=head1 SEE ALSO
See L<AI::CBR> for an overview of the framework.
lib/AI/CBR/Case/Compound.pm view on Meta::CPAN
=head1 METHODS
=head2 new
Creates a new compound case specification.
Pass a list of hash references as argument.
Each hash reference is the same specification as passed to L<AI::CBR::Case>.
=cut
sub new {
my ($class, @definitions) = @_;
# set default weights if unspecified
foreach my $attributes (@definitions) {
foreach (keys %$attributes) {
$attributes->{$_}->{weight} = $DEFAULT_WEIGHT unless defined $attributes->{$_}->{weight};
}
}
my $self = \@definitions;
lib/AI/CBR/Case/Compound.pm view on Meta::CPAN
=head2 set_values
Pass a flat hash of attribute keys and values.
This will overwrite existing values, and can thus be used as a faster method
for generating new cases with the same specification.
Notice that keys in the different specifications of the compound object may not have the same name!
=cut
sub set_values {
my ($self, %values) = @_;
foreach my $spec (@$self) {
foreach (keys %$spec) {
$spec->{$_}->{value} = $values{$_};
}
}
}
=head1 SEE ALSO
lib/AI/CBR/Retrieval.pm view on Meta::CPAN
=head2 new
Creates a new object for retrieval.
Pass your case specification object as the first parameter.
Pass the reference of an array of hash references as the case-base.
The hashes should contain all attributes of the specification.
These will be called candidate cases internally.
=cut
sub new {
my ($classname, $spec, $candidates) = @_;
croak('new case without candidates') unless @$candidates;
my $self = {
candidates => $candidates,
# we accept single specs as hash-ref or composed specs as array-ref
# internally both will be handled as a composed array-ref
queries => ref $spec eq 'AI::CBR::Case' ? [$spec] : $spec,
};
bless $self, $classname;
return $self;
lib/AI/CBR/Retrieval.pm view on Meta::CPAN
=head2 compute_sims
If the case-specification is complete,
you may call this method to compute the similarities
of all candidate cases to this specification.
After this step, each candidate of the case-base will have an
additional attribute C<_sim> indicating the similarity.
=cut
sub compute_sims {
my ($self) = @_;
# pre-allocate variables used in loop
my ($sum_sims, $sum_weights, $att_key, $att, $weight, $x, $y);
my $num_queries = int @{$self->{queries}};
foreach my $candidate (@{$self->{candidates}}) {
$candidate->{_sim} = 1;
foreach my $query (@{$self->{queries}}) {
$sum_sims = 0;
lib/AI/CBR/Retrieval.pm view on Meta::CPAN
Use one of these methods to get the similar cases you are interested into.
=head3 most_similar_candidate
Returns the most similar candidate.
No parameters.
=cut
sub most_similar_candidate {
my ($self) = @_;
return $self->{candidates}->[0];
}
=head3 n_most_similar_candidates
Returns the n most similar candidates.
n is the only parameter.
=cut
sub n_most_similar_candidates {
my ($self, $n) = @_;
my $last_index = min($n - 1, int @{$self->{candidates}});
return map { $self->{candidates}->[$_] } (0 .. $last_index);
}
=head3 first_confirmed_candidate
Returns the first candidate that is confirmed by a later candidate.
Confirmation is based on an attribute value
whose key is passed as parameter.
In case there is no confirmed candidate at all,
simply returns the most similar one.
=cut
sub first_confirmed_candidate {
my ($self, $key) = @_;
my %candidate_with;
my $value;
foreach my $candidate (@{$self->{candidates}}) {
$value = $candidate->{$key};
if($candidate_with{$value}) {
return $candidate_with{$value};
} else {
$candidate_with{$value} = $candidate;
}
}
# no confirmed candidate found, fall back
return $self->most_similar_candidate();
}
# internal method for n-th root
sub _nrt {
return $_[1] ** (1 / $_[0]);
}
=head1 SEE ALSO
See L<AI::CBR> for an overview of the framework.
=head1 AUTHOR
lib/AI/CBR/Sim.pm view on Meta::CPAN
Works for any numeric values.
Suitable when you are interested into the difference of values in a given range.
Returns the fraction of the difference of the values with respect to a given maximum range of interest.
The madatory third argument is this range.
sim_dist(26, 22, 10); # returns 0.4
sim_dist(-2, 1, 100); # returns 0.03
=cut
sub sim_dist {
my ($a, $b, $range) = @_;
return 1 if $a == $b;
my $dist = abs($a - $b);
return 0 if $dist >= $range;
return 1 - $dist / $range;
}
=head2 sim_frac
Works for non-negative numeric values.
Suitable when you are only interested into their relative difference with respect to 0.
Returns the fraction of the smaller argument with respect to the higher one.
sim_frac(3, 2); # returns 0.67
sim_frac(40, 50); # returns 0.8
=cut
sub sim_frac {
my ($a, $b) = @_;
return 1 if $a == $b;
return 0 if $a * $b == 0;
return $a > $b ? $b / $a : $a / $b;
}
=head2 sim_eq
Works for any textual value.
Suitable when you are interested only into equality/inequality.
Returns 1 in case of equality, 0 in case of inequality.
No third argument.
sim_eq('foo', 'bar'); # returns 0
sim_eq('foo', 'foo'); # returns 1
=cut
sub sim_eq {
return $_[0] eq $_[1] ? 1 : 0;
}
=head2 sim_set
Works for sets/lists of textual values.
Suitable when you are interested into overlap of the two sets.
Arguments are two array references with textual values.
Returns the number of elements in the intersection
divided by the number of elements in the union.
No third argument.
sim_set([qw/a b c/], [qw/b c d/]); # returns 0.5
sim_set([qw/a b c/], [qw/c/]); # returns 0.33
=cut
sub sim_set {
my ($a, $b) = @_;
return 1 if int @$a == 0 && int @$b == 0;
return 0 unless int @$a && int @$b;
my %a = map { ($_ => 1) } @$a;
my $union = int keys %a;
my $intersection = 0;
map {
$a{$_} ? $intersection++ : $union++
} @$b;
return $intersection / $union;
t/boilerplate.t view on Meta::CPAN
#!perl -T
use strict;
use warnings;
use Test::More tests => 4;
sub not_in_file_ok {
my ($filename, %regex) = @_;
open( my $fh, '<', $filename )
or die "couldn't open $filename for reading: $!";
my %violated;
while (my $line = <$fh>) {
while (my ($desc, $regex) = each %regex) {
if ($line =~ $regex) {
push @{$violated{$desc}||=[]}, $.;
t/boilerplate.t view on Meta::CPAN
}
if (%violated) {
fail("$filename contains boilerplate text");
diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
} else {
pass("$filename contains no boilerplate text");
}
}
sub module_boilerplate_ok {
my ($module) = @_;
not_in_file_ok($module =>
'the great new $MODULENAME' => qr/ - The great new /,
'boilerplate description' => qr/Quick summary of what the module/,
'stub function definition' => qr/function[12]/,
);
}
TODO: {
local $TODO = "Need to replace the boilerplate text";
( run in 0.262 second using v1.01-cache-2.11-cpan-a5abf4f5562 )