AI::NeuralNet::SOM::Hexa - Perl extension for Kohonen Maps (hexagonal topology)


AI-NeuralNet-SOM documentation Contained in the AI-NeuralNet-SOM distribution.

Index


Code Index:

NAME

Top

AI::NeuralNet::SOM::Hexa - Perl extension for Kohonen Maps (hexagonal topology)

SYNOPSIS

Top

  use AI::NeuralNet::SOM::Hexa;
  my $nn = new AI::NeuralNet::SOM::Hexa (output_dim => 6,
                                         input_dim  => 3);
  # ... see also base class AI::NeuralNet::SOM

INTERFACE

Top

Constructor

The constructor takes the following arguments (additionally to those in the base class):

output_dim : (mandatory, no default)

A positive, non-zero number specifying the diameter of the hexagonal. 1 creates one with a single hexagon, 2 one with 4, 3 one with 9. The number plays the role of a diameter.

Example:

    my $nn = new AI::NeuralNet::SOM::Hexa (output_dim => 6,
                                           input_dim  => 3);

Methods

radius

Returns the radius (half the diameter).

diameter

Returns the diameter (= dimension) of the hexagon.

map

$m = $nn->map

This method returns the 2-dimensional array of vectors in the grid (as a reference to an array of references to arrays of vectors).

Example:

   my $m = $nn->map;
   for my $x (0 .. $nn->diameter -1) {
       for my $y (0 .. $nn->diameter -1){
           warn "vector at $x, $y: ". Dumper $m->[$x]->[$y];
       }
   }

This array represents a hexagon like this (ASCII drawing is so cool):

               <0,0>
           <0,1>   <1,0>
       <0,2>   <1,1>   <2,0>
   <0,3>   <1,2>   <2,1>   <3,0>
  ...............................




as_string

Not implemented.

as_data

Not implemented.

AUTHOR

Top

Robert Barta, <rho@devc.at>

COPYRIGHT AND LICENSE

Top


AI-NeuralNet-SOM documentation Contained in the AI-NeuralNet-SOM distribution.
package AI::NeuralNet::SOM::Hexa;

use strict;
use warnings;

use AI::NeuralNet::SOM;
use Data::Dumper;
use base qw(AI::NeuralNet::SOM);

use AI::NeuralNet::SOM::Utils;

sub new {
    my $class = shift;
    my %options = @_;
    my $self = bless { %options }, $class;

    if ($self->{output_dim} > 0) {
	$self->{_D} = $self->{output_dim};
    } else {
	die "output dimension must be positive integer";
    }
    if ($self->{input_dim} > 0) {
	$self->{_Z} = $self->{input_dim};
    } else {
	die "input dimension must be positive integer";
    }

    $self->{_R}      = $self->{_D} / 2;
    $self->{_Sigma0} = $options{sigma0}        || $self->{_R};                                  # impact distance, start value
    $self->{_L0}     = $options{learning_rate} || 0.1;                                       # learning rate, start value

    return $self;
}

sub radius {
    my $self = shift;
    return $self->{_R};
}

sub diameter {
    my $self = shift;
    return $self->{_D};
}

sub initialize {
    my $self = shift;
    my @data = @_;

    our $i = 0;
    my $get_from_stream = sub {
	$i = 0 if $i > $#data;
	return [ @{ $data[$i++] } ];  # cloning !
    } if @data;
    $get_from_stream ||= sub {
	return [ map { rand( 1 ) - 0.5 } 1..$self->{_Z} ];
    };

    for my $x (0 .. $self->{_D}-1) {
	for my $y (0 .. $self->{_D}-1) {
	    $self->{map}->[$x]->[$y] = &$get_from_stream;
	}
    }
}

sub bmu {
    my $self = shift;
    my $sample = shift;

    my $closest;                                                               # [x,y, distance] value and co-ords of closest match
    for my $x (0 .. $self->{_D}-1) {
        for my $y (0 .. $self->{_D}-1){
	    my $distance = AI::NeuralNet::SOM::Utils::vector_distance ($self->{map}->[$x]->[$y], $sample);   # || Vi - Sample ||
#warn "distance to $x, $y : $distance";
	    $closest = [0,  0,  $distance] unless $closest;
	    $closest = [$x, $y, $distance] if $distance < $closest->[2];
	}
    }
    return @$closest;
}

sub neighbors {                                                               # http://www.ai-junkie.com/ann/som/som3.html
    my $self = shift;
    my $sigma = shift;
    my $X     = shift;
    my $Y     = shift;     

    my @neighbors;
    for my $x (0 .. $self->{_D}-1) {
        for my $y (0 .. $self->{_D}-1){
            my $distance = _hexa_distance ($X, $Y, $x, $y);
##warn "$X, $Y, $x, $y: distance: $distance";
	    next if $distance > $sigma;
	    push @neighbors, [ $x, $y, $distance ];                                    # we keep the distances
	}
    }
    return \@neighbors;
}

sub _hexa_distance {
    my ($x1, $y1) = (shift, shift);   # one point
    my ($x2, $y2) = (shift, shift);   # another

    ($x1, $y1, $x2, $y2) = ($x2, $y2, $x1, $y1)  # swapping
	if ( $x1+$y1 > $x2+$y2 );

    my $dx = $x2 - $x1;
    my $dy = $y2 - $y1;

    if ($dx < 0 || $dy < 0) {
	return abs ($dx) + abs ($dy);
    } else {
	return $dx < $dy ? $dy : $dx;
    }
}

## TODO: pretty printing of this as hexagon ?
sub as_string { die "not implemented"; }

sub as_data { die "not implemented"; }

our $VERSION = '0.02';

1;

__END__



sub _get_coordinates {
    my $self = shift;
    my $D1 = $self->{_D}-1;
    my $t;
    return map { $t = $_ ; map { [ $t, $_ ] } (0 .. $D1) } (0 .. $D1)
}

sqrt ( ($x - $X) ** 2 + ($y - $Y) ** 2 );