AI-FuzzyEngine

 view release on metacpan or  search on metacpan

lib/AI/FuzzyEngine.pm  view on Meta::CPAN

    $_PDL_is_imported = 1;
}

sub _cat_array_of_piddles {
    my ($class, @vals)  = @_;

    # TODO: Rapid return if @_ == 1 (isa piddle)
    # TODO: join "-", ndims -> Schnellcheck auf gleiche Dim.

    # All elements must get piddles
    my @pdls  = map { PDL::Core::topdl($_) } @vals;

    # Get size of wrapping piddle (using a trick)
    # applying valid expansion rules for element wise operations
    my $zeros = PDL->pdl(0);
    #        v-- does not work due to threading mechanisms :-((
    # $zeros += $_ for @pdls;
    # Avoid threading!
    for my $p (@pdls) {
        croak "Empty piddles are not allowed" if $p->isempty();
        eval { $zeros = $zeros + $p->zeros(); 1
            } or croak q{Can't expand piddles to same size};
    }

    # Now, cat 'em by expanding them on the fly
    my $vals = PDL::cat( map {$_ + $zeros} @pdls );
    return $vals;
};

1;

=pod

=head1 NAME

AI::FuzzyEngine - A Fuzzy Engine, PDL aware

lib/AI/FuzzyEngine.pm  view on Meta::CPAN

(see C<< AI::FuzzyEngine->_cat_array_of_piddles(@pdls) >>).
Assuming all piddles are in C<@pdls>,
calculation goes as follows:

    # Get the common dimensions
    my $zeros = PDL->pdl(0);
    # Note: $zeros += $_->zeros() for @pdls does not work here
    $zeros = $zeros + $_->zeros() for @pdls;

    # Expand all piddles
    @pdls = map {$_ + $zeros} @pdls;

Defuzzification uses some heavy non-threading code,
so there might be a performance penalty for big piddles. 

=head2 Todos

=over 2

=item Add optional alternative implementations of fuzzy operations

lib/AI/FuzzyEngine/Set.pm  view on Meta::CPAN

    my $funB = shift @moreFuns;
    my $fun  = $class->_minmax_of_pair_of_funs( $factor, $funA, $funB );

    # solve recursively
    return $class->_minmax_of_funs( $factor, $fun, @moreFuns );
}

sub min_of_funs {
    my ($class, @funs) = @_;
    # Copy can not moved to _minmax_of_funs (is recursively called)
    my @copied_funs = map { $class->_copy_fun($_) } @funs;
    return $class->_minmax_of_funs( -1, @copied_funs );
}

sub max_of_funs {
    my ($class, @funs) = @_;
    # Copy can not moved to _minmax_of_funs (is recursively called)
    my @copied_funs = map { $class->_copy_fun($_) } @funs;
    return $class->_minmax_of_funs( 1, @copied_funs );
}

sub clip_fun {
    my ($class, $fun, $max_y) = @_;

    # clip by min operation on function $fun
    my @x         = _x_of $fun;
    my @y         = ( $max_y ) x @x;
    my $fun_limit = [ \@x => \@y ];

lib/AI/FuzzyEngine/Variable.pm  view on Meta::CPAN

    };
    return;
}

sub defuzzify {
    my ($self)  = @_;
    croak "Defuzzification not allowed for internal variables"
        if $self->is_internal;

    my @sets    = values %{$self->sets};
    my @funs    = map { $_->memb_fun } @sets;
    my @degrees = map { $_->degree   } @sets;

    # If all degrees are real scalars a shortcut is possible
    if (_non_is_a_piddle(@degrees)) {
        my $funs    = _clipped_funs( \@funs, \@degrees);
        my $fun_agg = $set_class->max_of_funs( @$funs );
        my $c       = $set_class->centroid( $fun_agg );
        return $c;
    };

    # Need a function of my FuzzyEngine
    my $fe = $self->fuzzyEngine;
    die 'Internal: fuzzy_engine is lost' unless $fe;

    # Unify dimensions of all @degrees (at least one is a pdl)
    my @synched_degrees = $fe->_cat_array_of_piddles(@degrees)->dog;
    my @dims_to_reshape = $synched_degrees[0]->dims;

    # Make degrees flat to proceed them as lists
    my @flat_degrees    = map {$_->flat} @synched_degrees;
    my $flat_degrees    = PDL::cat( @flat_degrees );

    # Proceed degrees of @sets as synchronized lists
    my @degrees_per_el  = $flat_degrees->transpose->dog;
    my @defuzzified;
    for my $ix (reverse 0..$#degrees_per_el) {
        my $el_degrees = $degrees_per_el[$ix];
        # The next two lines cost much (75% of defuzzify)
        my $funs       = _clipped_funs( \@funs, [$el_degrees->list] );
        my $fun_agg    = $set_class->max_of_funs( @$funs );

t/01-fuzzyEngine.t  view on Meta::CPAN

    is( $s->degree, 0.3, 'Conjunction of multiple inputs ("and" operation)' );

    local $set_pars{memb_fun} = [ [0.2, 0.3, 0.8, 1.0], # x
                                  [0.1, 0.5, 0.5, 0.0], # y
                                ];
    $s = $set_class->new(%set_pars);

    # fuzzify some values
    my @vals     = (  0, 0.2, 0.25, 0.3, 0.5, 0.8, 0.90, 1);
    my @expected = (0.1, 0.1, 0.30, 0.5, 0.5, 0.5, 0.25, 0 );
    my @got      = map { $s->fuzzify($_) } @vals;
    is_deeply( \@got, \@expected,
               'fuzzify incl. corner cases and reset of degree',
             );

    my $degree = $s->fuzzify( 0.2 );
    is( $degree, 0.1, 'fuzzify returns degree' );

    $set_pars{memb_fun} = [ [0, 1, 1, 2] => [1, 2, 3, 4] ];
    throws_ok {$s = AI::FuzzyEngine::Set->new(%set_pars)
              } qr/no double/i, 'Checks double interpolation coordinates';

t/01-fuzzyEngine.t  view on Meta::CPAN

                            );

    # $v and $w have a 'low' function.
    # Are they independent with regard to degree?
    is( $v->low, 0.1, 'degree for low unchanged from other variables' );
    is( $w->low, 0,   'degree for low of the new variable is independent');
};

subtest "$var_class order of sets" => sub {
    my @range        = 0..99;
    my @list_of_sets = map { ("s_$_" => [$_,1]) } @range;

    my $x = $var_class->new( $fe, 0 => 1, @list_of_sets );
    my @indexes      = map {/(\d+)/} $x->set_names;

    no warnings qw(once);
    my @is_same = List::MoreUtils::pairwise {$a==$b} @range, @indexes;
    ok( ( List::MoreUtils::all {$_} @is_same ),
        q{set_names returns the set's names in correct range},
    );
};

subtest "$var_class completing membership functions in x" => sub {

t/02-fuzzyEngine-pdl_aware.t  view on Meta::CPAN


    # _cat_array_of_piddles

    my @vals = (0..2);
    my $vals = $class->_cat_array_of_piddles(@vals);
    is( $vals->ndims, 1, 'ndims of cat topdl with scalars');
    ok_all( $vals == pdl( [ 0, 1, 2 ] ),
            'cat topdl with scalars',
          );

    @vals = map {pdl([$_])} (0..2);
    $vals = $class->_cat_array_of_piddles(@vals);
    is( $vals->ndims, 2, 'ndims of cat topdl with pdl([scalar])' );
    ok_all( $vals == pdl( [ [0], [1], [2], ] ),
            'cat topdl with scalars',
          );

    @vals = map {pdl([[$_, 1], [7]])} (0..2);
    $vals = $class->_cat_array_of_piddles(@vals);
    is( $vals->ndims, 3, 'cat of 2dim' );

    @vals =( 6, pdl( [[5, 7], [1, 2]] ) );
    $vals = $class->_cat_array_of_piddles(@vals);
    ok_all( $vals == pdl( [[6, 6], [6, 6]],
                          [[5, 7], [1, 2]],
                        ),
            'cat topdl scalar, 2dim 4elem pdl',
          ) or diag $vals;



( run in 0.461 second using v1.01-cache-2.11-cpan-49f99fa48dc )