AI-FuzzyEngine

 view release on metacpan or  search on metacpan

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

        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 );

        my $c          = $set_class->centroid( $fun_agg );
        $defuzzified[$ix] = $c;
    };

    # Build result in shape of unified membership degrees
    my $flat_defuzzified = PDL->pdl( @defuzzified );
    my $defuzzified      = $flat_defuzzified->reshape(@dims_to_reshape);
    return $defuzzified;
}

sub _clipped_funs {
    # Clip all membership functions of a variable
    # according to the respective membership degree (array of scalar)
    my ($funs, $degrees) = @_;
    my @funs    = @$funs;    # Dereferencing here saves some time
    my @degrees = @$degrees;
    my @clipped = List::MoreUtils::pairwise {
                     $set_class->clip_fun($a => $b)
                  } @funs, @degrees;
    return \@clipped;
}

sub reset {
    my ($self) = @_;
    $_->reset() for values %{$self->sets};
    return $self;
}

sub change_set {
    my ($self, $setname, $new_memb_fun) = @_;
    my $set = $self->set( $setname );

    # Some checks
    croak "Set $setname does not exist" unless defined $set;
    croak 'Variable is internal' if $self->is_internal;

    # Convert to internal representation
    my $fun = $self->_curve_to_fun( $new_memb_fun );

    # clip membership function to borders
    $set->set_x_limits( $fun, $self->from => $self->to );

    # Hand the new function over to the set
    $set->replace_memb_fun( $fun );

    # and reset the variable
    $self->reset;
    return;
}

sub _init {
    my ($self, @pars) = @_;

    croak "Too few arguments" unless @pars >= 2;

    # Test for internal variable
    my ($from, $to, @sets);
    if (looks_like_number $pars[0]) {
        # $from => $to is given
        $self->{is_internal} = '';
        ($from, $to, @sets)  = @pars;
    }
    else {
        $self->{is_internal} = 1;
        ($from, $to, @sets)  = (undef, undef, @pars);
    };

    # Store $from, $to ( undef if is_internal)
    $self->{from} = $from;
    $self->{to  } = $to;

    # Provide names of sets in correct order by attribute set_names
    my $ix = 1;
    $self->{set_names} = [ grep {$ix++ % 2} @sets ];


    # Build sets of the variable
    my %sets = @sets;
    SET_TO_BUILD:
    for my $set_name (keys %sets) {

        my $fun = [ [] => [] ]; # default membership function

        if (not $self->is_internal) {
            # Convert from set of points to [ \@x, \@y ] format
            my $curve = $sets{$set_name};
            $fun   = $self->_curve_to_fun( $curve );

            # clip membership function to borders
            $set_class->set_x_limits( $fun, $self->from => $self->to );
        };

        # create a set and store it
        my $set_class = $self->_class_of_set();
        my $set = $set_class
            ->new( fuzzyEngine => $self->fuzzyEngine,
                   variable    => $self,
                   name        => $set_name,
                   memb_fun    => $fun, # [ [] => [] ] if is_internal
              );
        $self->{sets}{$set_name} = $set;

        # build membership function if necessary
        next SET_TO_BUILD if $self->can( $set_name );
        my $method = sub {
            my ($variable, @vals) = @_; # Variable, fuzzy values
            my $set = $variable->{sets}{$set_name};
            return $set->degree( @vals );
        };

        # register the new method to $self (the fuzzy variable)
        no strict 'refs';
        *{ $set_name } = $method;
    };
}



( run in 0.612 second using v1.01-cache-2.11-cpan-9789f410c06 )