AI-NNFlex

 view release on metacpan or  search on metacpan

lib/AI/NNFlex/Hopfield.pm  view on Meta::CPAN

####################################################
# AI::NNFlex::Hopfield
####################################################
# Hopfield network simulator
####################################################
#
# Version history
# ===============
#
# 1.0	20050330	CColbourn	New module
#
####################################################
package AI::NNFlex::Hopfield;

use strict;
use AI::NNFlex;
use AI::NNFlex::Mathlib;
use Math::Matrix;
use base qw(AI::NNFlex AI::NNFlex::Mathlib);

####################################################
# AI::NNFlex::Hopfield::init
####################################################
#
# The hopfield network has connections from every
# node to every other node, rather than being 
# arranged in distinct layers like a feedforward
# network. We can retain the layer architecture to
# give us blocks of nodes, but need to overload init
# to perform full connections
#
#####################################################
sub init
{

	my $network = shift;
	my @nodes;

	# Get a list of all the nodes in the network
	foreach my $layer (@{$network->{'layers'}})
	{
		foreach my $node (@{$layer->{'nodes'}})
		{
			# cover the assumption that some inherited code
			# will require an activation function
			if (!$node->{'activationfunction'})
			{
				$node->{'activationfunction'}= 'hopfield_threshold';
				$node->{'activation'} =0;
				$node->{'lastactivation'} = 0;
			}
			push @nodes,$node;
		}
	}

	# we'll probably need this later
	$network->{'nodes'} = \@nodes;

	foreach my $node (@nodes)
	{
		my @connectedNodes;
		foreach my $connectedNode (@nodes)
		{
			push @connectedNodes,$connectedNode;
		}
		my @weights;
		$node->{'connectednodes'}->{'nodes'} = \@connectedNodes;
		for (0..(scalar @nodes)-1)
		{
			push @weights,$network->calcweight();
		}
		$node->{'connectednodes'}->{'weights'} = \@weights
	}

	return 1;

}

##########################################################
# AI::NNFlex::Hopfield::run
##########################################################
# apply activation patterns & calculate activation
# through the network
##########################################################
sub run
{
	my $network = shift;

	my $inputPatternRef = shift;

	my @inputpattern = @$inputPatternRef;

	if (scalar @inputpattern != scalar @{$network->{'nodes'}})
	{
		return "Error: input pattern does not match number of nodes"
	}

	# apply the pattern to the network
	my $counter=0;
	foreach my $node (@{$network->{'nodes'}})
	{
		$node->{'activation'} = $inputpattern[$counter];
		$counter++;
	}

	# Now update the network with activation flow
	foreach my $node (@{$network->{'nodes'}})
	{
		$node->{'activation'}=0;
		my $counter=0;
		foreach my $connectedNode (@{$node->{'connectednodes'}->{'nodes'}})
		{
			# hopfield nodes don't have recursive connections
			unless ($node == $connectedNode)
			{
				$node->{'activation'} += $connectedNode->{'activation'} * $node->{'connectednodes'}->{'weights'}->[$counter];

			}
			$counter++;
		}


		# bias
		$node->{'activation'} += 1 * $node->{'connectednodes'}->{'weights'}->[-1];

		my $activationfunction = $node->{'activationfunction'};
		$node->{'activation'} = $network->$activationfunction($node->{'activation'});

	}

	return $network->output;
}

#######################################################
# AI::NNFlex::Hopfield::output
#######################################################
# This needs to be overloaded, because the default
# nnflex output method returns only the rightmost layer
#######################################################
sub output
{
	my $network = shift;

	my @array;
	foreach my $node (@{$network->{'nodes'}})
	{
		unshift @array,$node->{'activation'};
	}

	return \@array;
}

########################################################
# AI::NNFlex::Hopfield::learn
########################################################
sub learn
{
	my $network = shift;

	my $dataset = shift;

	# calculate the weights
	# turn the dataset into a matrix
	my @matrix;
	foreach (@{$dataset->{'data'}})
	{
		push @matrix,$_;
	}
	my $patternmatrix = Math::Matrix->new(@matrix);

	my $inversepattern = $patternmatrix->transpose;

	my @minusmatrix;

	for (my $rows=0;$rows <(scalar @{$network->{'nodes'}});$rows++)
	{
		my @temparray;
		for (my $cols=0;$cols <(scalar	@{$network->{'nodes'}});$cols++)
		{
			if ($rows == $cols)
			{
				my $numpats = scalar @{$dataset->{'data'}};
				push @temparray,$numpats;	
			}
			else
			{
				push @temparray,0;
			}
		}
		push @minusmatrix,\@temparray;
	}

	my $minus = Math::Matrix->new(@minusmatrix);

	my $product = $inversepattern->multiply($patternmatrix);

	my $weights = $product->subtract($minus);

	my @element = ('1');
	my @truearray;
	for (1..scalar @{$dataset->{'data'}}){push @truearray,"1"}
	
	my $truematrix = Math::Matrix->new(\@truearray);

	my $thresholds = $truematrix->multiply($patternmatrix);
	#$thresholds = $thresholds->transpose();

	my $counter=0;
	foreach (@{$network->{'nodes'}})
	{
		my @slice;
		foreach (@{$weights->slice($counter)})
		{
			push @slice,$$_[0];
		}



( run in 0.524 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )