package Kosmos; our $VERSION = 1.0; $|=1; our $DEBUG=0; sub debug { print @_ if $DEBUG; } # TODO # duplicities in data files, e.g. same node or link labels # using capitals for labels in source files # numerical values checking -- now format \d+(.\d+)? # handling ltr link type # if number of active nodes is close to number of all nodes, for storig values of such nodes will be better an array # setting the threshold (probably from config file) # using different types of formulas # -node_numbers_to_labels -- bude lepsi pole misto hashe use strict; # initial attributes setting # parameters read from the config file, here, default values are set our $IterationsNo = 10; # how many iterations will be executer our $IterationStep = 1; # after how many iterations the output is needed our @Iterations; # in which iterations is the output needed our $calibration = 'None'; # type of calibration; 'ConservationOfTotalActivation', 'ConservationOfInitialActivation', 'None' or '' our $beta = 0.5; # affecting the strength of outcoming signal our $regression = 'linear'; # type of regression used by booster; 'linear' or 'quadratic' our $history_length = 3; # number of values in node history our $booster = 0; # whether to use booster/modified SA algorithm working with history and value interpolation our $lambda = 2; # affecting theimportance of interpolation for new node value our $mu = 1; # multiplicator for node inputs our $activation_decrease = 0; # how the node value is decreased when the signal is sent our $max_iterations_with_history = 1000; # when input/output operation stops with booster our $max_output_iterations = 10; # maximal number of iterations when nodes can send signal our $max_input_iterations = 10; # maximal number of iterations when nodes can receive signal our %number_of_output_iterations_of_nodes; # key = node number, value = number of times when the node sent signal our %number_of_input_iterations_of_nodes; # key = node number, value = number of times when the node received signal our $config_files_set = 0; # the flag telling whether config file names are set our $config_files_separator = '\s+'; # value separating values in config files our $data_ok = 0; # the flag telling whether the task is correctly described by input data our @total_init_activation; # individual elements contain sums of initial activation on individual elements of activation vector our @active_nodes_sum; # individual elements contain sums of total activation on individual elements of activation vector on activated nodes; is used for calibration our @list_of_initially_activated_nodes; # contains a list of nodes that were initially activated our @sum_of_initially_activated_nodes; # individual elements contain sums of total activation on individual elements of activation vector on initially activated nodes; is used for calibration # network information our %nodes; # keys are node names, values their importance our %active_nodes; # keys are node numbers, values are arrays of activation on given node our @links; # position of the array element = number of initial node, element contains arrays [number of terminal node, link type] our @link_types; # position = link type number, values are weights our @node_numbers_to_labels; # indexes = node numbers, values = node names our %node_labels_to_numbers; # keys = node labels, values = node numbers our %link_types_to_numbers; # keys = link type name, value = link type number our %node_types; # keys = types of nodes, values = 1 our $number_of_nodes = 0; # number of nodes our $number_of_links = 0; # number of links # CGF files information our $alg_cfg_file; # file with algorithm configuration our $data_cfg_file; # file with data configuration our $network_cfg_file; # file with network configuration our $data_file; # file with network data our $init_activation_file; # file with initial activation our @threshold=(0); # thresholds used for purging the list of activated nodes sub set_config_files_separator { my ($sep) = @_; unless ($sep) { warn "Config file separator not provided\n"; } else { $config_files_separator = $sep; } } sub set_config_files { # -cfg_files => { # -alg_cfg => description of the algorithm # -data_cfg => data configuration (node and link types) # -init_activation => initial activation of the network # -data => network data # -network_cfg => network configuration (link weights) # } my (%arg) = @_; $alg_cfg_file = $arg{-cfg_files}->{-alg_cfg}; $data_cfg_file = $arg{-cfg_files}->{-data_cfg}; $network_cfg_file = $arg{-cfg_files}->{-network_cfg}; $data_file = $arg{-cfg_files}->{-data}; $init_activation_file = $arg{-cfg_files}->{-init_activation}; $config_files_set = 1; } sub spreading_activation { @active_nodes_sum = @total_init_activation; # topology of network for visualization print print_nodes(), "\n"; # node labels print join("\t", 'iter.', sort keys %nodes), "\n"; # initial node values print join("\t", 0, map {join '-', @{value_of_node_calibrated($_)}} sort keys %nodes), "\n"; for my $iteration (1..$IterationsNo) { spreading_activation_iteration(); #use Data::Dumper; print Dumper \%active_nodes; if (($IterationStep && not $iteration % $IterationStep) or $iteration == $IterationsNo or (defined @Iterations && scalar @Iterations) && grep {$_==$iteration} @Iterations) { # presenting requested results # toto je jen pro prvni hodnotu print join "\t", "$iteration", map {value_of_node($_)->[0]} sort keys %nodes; =pod # fisrt value in the vector and history print "$iteration\t"; for my $k (sort keys %{$self->{-nodes}}) { printf '%.5f', ($self->value_of_node($k)->[0] || 0); #print "\t"; if (exists $self->{-active_nodes}->{$self->node_number($k)}) { print '+[', join ',', map {sprintf '%.3f', $_} @{$self->{-active_nodes}->{$self->node_number($k)}->[1]}; print "]"; } print "\t"; } =cut print "\n"; } warn "iteration $iteration completed\n"; warn scalar keys %active_nodes, " activated nodes\n" } } sub spreading_activation_iteration { my %active_links; ################## # list expansion # ################## for my $node (keys %active_nodes) { for my $term_node (@{$links[$node]}) { # node connected through outgoing link # $term_node is an array [TERMINAL NODE NUMBER, LINK TYPE] # adding a new node to activated nodes $active_nodes{$term_node->[0]} = [] unless exists $active_nodes{$term_node->[0]}; } } debug "active nodes\n", join (', ', sort map {node_label($_)} keys %active_nodes), "\n"; ################# # recomputation # ################# for my $active_node (keys %active_nodes) { # skip nodes with zero values, just activated nodes -- nothing can spread from these nodes (TODO: this might change in the future) next unless $active_nodes{$active_node}->[0]; # when a node sent a signal for given number of iterations, it is skipped next if defined $max_output_iterations and exists $number_of_output_iterations_of_nodes{$active_node} and $number_of_output_iterations_of_nodes{$active_node} >= $max_output_iterations; # increasing the number of participations in sending the signal $number_of_output_iterations_of_nodes{$active_node}++ if defined $max_output_iterations; # the sum of sent signal, TODO: currently only for the firts value my $output; debug "before input : $active_nodes{$active_node}->[0]\n"; # setting input values for all outgoing links from active node for my $link (@{$links[$active_node]}) { # $link is [TERMINAL NODE NUMBER, LINK TYPE] # when a node received a signal for given number of iterations, it is skipped next if defined $max_input_iterations and exists $number_of_input_iterations_of_nodes{$link->[0]} and $number_of_input_iterations_of_nodes{$link->[0]} >= $max_input_iterations; # setting the input vectors for active links my @temp_link = ($link->[1]); # link type for my $element (0..$#{$active_nodes{$active_node}}) { # going through all values of active node vector # first element, normal SA if ($element == 0) { my $temp = $active_nodes{$active_node}->[$element] # value of active node / (outdegree($active_node) # number of outgoing links ** $beta); push @temp_link, $temp; $output += $temp; } # third element, activation history, is not sent } push @{$active_links{$link->[0]}}, [@temp_link]; } # decreasing node activation level debug "node ",node_label($active_node),", $active_nodes{$active_node}->[0], decreasing by ",($activation_decrease*$output),"\n"; $active_nodes{$active_node}->[0] = ($activation_decrease*$output > $active_nodes{$active_node}->[0] ? 0 : $active_nodes{$active_node}->[0] - $activation_decrease*$output); debug "node ",node_label($active_node),", after decrease $active_nodes{$active_node}->[0]\n"; } # setting the output values of vectors in links for my $term (keys %active_links) { # $active_links{$term} is [[LINK TYPE, INITIAL VALUES], ...] for my $link (@{$active_links{$term}}) { # $link is [LINK TYPE, INITIAL VALUES] # decay of activation for (1..$#$link) { # first value normally $link->[$_] = $link->[$_]*$link_types[$link->[0]] if $_ == 1; } } } # input and output value for all links leading from nodes with non zero value set # sum of all active nodes @active_nodes_sum = (); @sum_of_initially_activated_nodes = (); #### TODO - some links might not require the weights # setting new values for nodes for my $active_node (keys %active_links) { # $active_links{$active_node} is [[LINK TYPE NUMBER, LINK OUTPUT VALUES], [LINK TYPE NUMBER, LINK OUTPUT VALUES], ...], # contains all incoming links for $active_node # sum of incoming signal my @sum; # going through all links coming to the node for my $link (@{$active_links{$active_node}}) { # $link is [LINK TYPE NUMBER, LINK OUTPUT VALUES] # aplikace ruznych algoritmu na jednotlive prvky vektoru vstupujiciho do uzlu for my $element (1..$#$link) { if ($element == 1) { # first value normally $sum[$element-1] += $link->[$element]; } } } # setting new value for the node $number_of_input_iterations_of_nodes{$active_node}++ if defined $max_input_iterations; if ($booster and defined @{$active_nodes{$active_node}->[1]} and @{$active_nodes{$active_node}->[1]} == $history_length) { # history is full, the vector with historical values is modified (last value removed, new inserted) my $r = regression(@{$active_nodes{$active_node}->[1]}); # current value my $new = $active_nodes{$active_node}->[0] + ($active_nodes{$active_node}->[0] - $r)/$lambda; $new += $sum[0]*$mu if $active_nodes{$active_node}->[3]++ < $max_iterations_with_history; shift @{$active_nodes{$active_node}->[1]}; push @{$active_nodes{$active_node}->[1]}, $new; $active_nodes{$active_node}->[0] = $new; } else { $active_nodes{$active_node}->[0] += $sum[0]* $mu; debug "new value of a node ".node_label($active_node)." $active_nodes{$active_node}->[0]\n"; } =pod # noise if (exists $self->{-noise_activation}->{$self->{-iteration}} and grep {$_ eq $self->node_label($active_node)} @{$self->{-noise_activation}->{$self->{-iteration}}}) { $self->{-active_nodes}->{$active_node}->[0] *= 5; } =cut } # sum of all nodes - only for some kind of calibration if (lc $calibration eq 'conservationoftotalactivation') { for my $active_node (keys %active_nodes) { # sum of all active nodes - TODO: only for some kind of calibration $active_nodes_sum[0] += $active_nodes{$active_node}->[0]; } } # sum of initially activated nodes - only for some kind of calibration if (lc $calibration eq 'conservationofinitialactivation') { for my $init_node (@list_of_initially_activated_nodes) { $sum_of_initially_activated_nodes[0] += $active_nodes{$init_node}->[0]; } } # when the sum of initially activated nodes equals zero in the calibration mode ConservationOfInitnialActivation # there is problem in data calculated by the Booster and we cannot provide results if ($sum_of_initially_activated_nodes[0] == 0 and lc $calibration eq 'conservationofinitialactivation') { die "Booster is not able to provide correct results because of the character of data and algorithm parameters."; } # node calibration for my $active_node (keys %active_nodes) { my $pom = value_of_node_calibrated(node_label($active_node))->[0]; $active_nodes{$active_node}->[0] = $pom > 0 ? $pom : 0; } if ($booster) { # if the history is not full, new values must be stored for my $active_node (keys %active_nodes) { if (not defined @{$active_nodes{$active_node}->[1]} or @{$active_nodes{$active_node}->[1]} < $history_length) { push @{$active_nodes{$active_node}->[1]}, value_of_node(node_label($active_node))->[0]; } } } ################ # list purging # ################ for my $active_node (keys %active_nodes) { my $below = 1; for (0..$#{$active_nodes{$active_node}}) { if ($active_nodes{$active_node}->[$_] > $threshold[$_]) { # at least one value of the vector is greater than the threshold $below = 0; last; # TODO: remove whole vector or just one element? } } delete $active_nodes{$active_node} if $below; } } sub outdegree { my ($node) = @_; return scalar @{$links[$node]}; } sub regression { # returns following value for a list of values # array @_ contains the y values, no copies are created my @x = (1..scalar @_); if ($regression eq 'linear') { # linear regression my $n = scalar @_; my $sumx = eval join '+', @x; my $sumy = eval join '+', @_; my $sumx2 = eval join '+', map {$_*$_} @x; my $sumxy = eval join '+', map {$_[$_]*$x[$_]} 0..$#_; debug "input @_: sumx $sumx, sumy $sumy, sumx2 $sumx2, sumxt $sumxy\n"; my $b1 = ($n*$sumxy - $sumx*$sumy) / ($n*$sumx2 - $sumx*$sumx); my $b2 = $sumy/$n - $b1*$sumx/$n; return $b1*($n+1) + $b2 > 0 ? $b1*($n+1) + $b2 : 0; } elsif ($regression eq 'quadratic') { # quadratic regression my $sumx = eval join '+', @x; my $sumx2 = eval join '+', map {$_**2} @x; my $sumx3 = eval join '+', map {$_**3} @x; my $sumx4 = eval join '+', map {$_**4} @x; my $sumy = eval join '+', @_; my $sumyx = eval join '+', map {$_[$_]*$x[$_]} 0..$#_; my $sumyx2 = eval join '+', map {$_[$_]*$x[$_]**2} 0..$#_; use Math::Matrix; my $m = new Math::Matrix ([$sumx4, $sumx3, $sumx2, $sumyx2], [$sumx3, $sumx2, $sumx, $sumyx], [$sumx2, $sumx, scalar(@_), $sumy]); my $coeff = $m->solve; my $res = $coeff->[0][0]*(scalar @_+1)**2 + $coeff->[1][0]*(scalar @_+1) + $coeff->[2][0]; open W, ">>reg.txt"; print W "hodnoty ", join ', ',map {sprintf'%.5f', $_} @_; print W " prokladam $res\n"; return $res > 0 ? $res : 0; } } sub active_nodes_calibrated { # returns labels of active nodes and their calibrated values my %result; # calculation of normalization factor my @nf; if (lc $calibration eq 'conservationofinitialactivation') { push @nf, $total_init_activation[$_] / $sum_of_initially_activated_nodes[$_] for 0..$#total_init_activation; } # preparing labels for node numbers for my $active_node (keys %active_nodes) { my @values; if (lc $calibration eq 'conservationoftotalactivation') { push @values, $active_nodes{$active_node}->[$_] / $active_nodes_sum[$_] * $total_init_activation[$_] for 0..$#{$active_nodes_sum[$_]}; } if (lc $calibration eq 'conservationofinitialactivation') { push @values, $active_nodes{$active_node}->[$_] * $nf[$_] for 0..$#nf; } if (lc $calibration eq 'none') { push @values, $active_nodes{$active_node}->[$_] for 0..$#{$active_nodes{$active_node}}; } $result{$node_numbers_to_labels[$active_node]} = [@values]; } return \%result; } sub active_nodes_labels { # returns labels of active nodes and their values my @result; # preparing labels for node numbers for my $active_node (keys %active_nodes) { push @result, $node_numbers_to_labels[$active_node]; } return \@result; } sub number_of_active_nodes { # returns the number of active nodes return scalar keys %active_nodes; } sub number_of_nodes { # returns the number of nodes return scalar keys %nodes; } sub value_of_node { # returns the value of the node with given label my ($label) = @_; return $active_nodes{node_number($label)} || [sprintf '%.5f', 0]; } sub value_of_node_calibrated { # returns a value of node with given label my ($label) = @_; return [(sprintf '%.5f', 0) x @total_init_activation] unless exists $active_nodes{$node_labels_to_numbers{$label}}; if (lc $calibration eq 'conservationoftotalactivation') { my @result; for (0..$#total_init_activation) { # TODO: skipping non-scalar values next if ref $active_nodes{$node_labels_to_numbers{$label}}->[$_]; if ($_ == 0) { # normal SA push @result, sprintf '%.5f', $active_nodes{node_number($label)}->[$_] / $active_nodes_sum[$_] * $total_init_activation[$_] ; } else { # other values are not calibrated push @result, sprintf '%.5f', $active_nodes{node_number($label)}->[$_]; } } return [@result]; } if (lc $calibration eq 'conservationofinitialactivation') { my @result; for (0..$#{$active_nodes{node_number($label)}}) { # currently calibrating just the first value # TODO: skipping non-scalar values next if ref $active_nodes{node_number($label)}->[$_]; if ($_ == 0) { # normal SA push @result, sprintf '%.5f', $active_nodes{node_number($label)}->[$_] * $total_init_activation[$_] / $sum_of_initially_activated_nodes[$_]; } else { # other values are not calibrated push @result, sprintf '%.5f', $active_nodes{node_number($label)}->[$_]; } } return [@result]; } if (lc $calibration eq 'none' or not $calibration) { my @result; for (0..$#{$active_nodes{$node_labels_to_numbers{$label}}}) { # TODO: skipping non-scalar values next if ref $active_nodes{$node_labels_to_numbers{$label}}->[$_]; push @result, sprintf '%.5f', $active_nodes{$node_labels_to_numbers{$label}}->[$_] ; } return [@result]; } } sub node_label { my ($node) = @_; return $node_numbers_to_labels[$node]; } sub node_number { my ($node) = @_; return $node_labels_to_numbers{$node}; } sub read_config_files { # -check => 1 just checking the format and others my %arg = @_; unless ($config_files_set) { # config files were not set print "Set the config files first\n"; return; } =pod #deleting current network configuration delete $self->{$_} for qw/-beta -IterationsNo -link_types_to_numbers -reciprocal_links -link_types -links -node_types -nodes -node_labels_to_numbers -node_numbers_to_labels -active_nodes -number_of_links -number_of_nodes/; =cut $data_ok = 0; my $error; my %reciprocal_links; # checking config data for correctness: # - correct row format # unless (open F, $alg_cfg_file) { warn "Cannot open SAM algorithm configuration file $alg_cfg_file.\n"; $error = 1; } else { while () { # skip comments and rows with whitespaces only next if /^#/ or /^\s*$/; # remove comments and new line characters s/#.*//; chomp; # determining the keyword my $kw = /(\w+)/ ? $& : ''; next unless $&; if (lc $kw eq 'beta') { # coefficient beta my (undef, $_beta) = split /$config_files_separator/; $beta = $_beta; } elsif (lc $kw eq 'iterationsno') { # number of iterations my (undef, $i) = split /$config_files_separator/; $IterationsNo = $i; } elsif (lc $kw eq 'iteration') { # iterations to be presented my (undef, $i) = split /$config_files_separator/; push @Iterations, $i; } elsif (lc $kw eq 'iterationstep') { # step for iterations presentation my (undef, $i) = split /$config_files_separator/; $IterationStep = $i; } elsif (lc $kw eq 'calibration') { my (undef, $c) = split /$config_files_separator/; $calibration = $c; } elsif (lc $kw eq 'regression') { my (undef, $i) = split /$config_files_separator/; $regression = $i; } elsif (lc $kw eq 'booster') { my (undef, $i) = split /$config_files_separator/; $booster = $i; } elsif (lc $kw eq 'lambda') { my (undef, $i) = split /$config_files_separator/; $lambda = $i; $lambda = 1e1000 if $lambda < 0; # negative value = infinity } elsif (lc $kw eq 'mu') { my (undef, $i) = split /$config_files_separator/; $mu = $i; } elsif (lc $kw eq 'max_iterations_with_history') { my (undef, $i) = split /$config_files_separator/; $max_iterations_with_history = $i; } elsif (lc $kw eq 'max_output_iterations') { my (undef, $i) = split /$config_files_separator/; $max_output_iterations = $i; $max_output_iterations = 1e1000 if $max_output_iterations < 0; # negative value = infinity } elsif (lc $kw eq 'max_input_iterations') { my (undef, $i) = split /$config_files_separator/; $max_input_iterations = $i; $max_input_iterations = 1e1000 if $max_input_iterations < 0; # negative value = infinity } elsif (lc $kw eq 'history_length') { my (undef, $i) = split /$config_files_separator/; $history_length = $i; } elsif (lc $kw eq 'activation_decrease') { my (undef, $i) = split /$config_files_separator/; $activation_decrease = $i; } } close F; # TODO: checking mandatory parameters unless ($IterationsNo) { warn "Number of iterations not entered\n"; $error++; } } # checking network types configuration file for correctness: # - correct row format: # node type: nt node_type # link type: lt link_type_label [reciprocal] # unless (open F, $data_cfg_file) { warn "Cannot open data configuration file $data_cfg_file.\n"; $error = 1; } else { my $link_type_number = 0; while () { # skip comments and rows with whitespaces only next if /^#/ or /^\s*$/; # remove comments and new line characters s/#.*//; chomp; # determining the keyword my $kw = /(\w+)/ ? $& : ''; if (lc $kw eq 'nt') { # node type my (undef, $node_type) = split /$config_files_separator/; unless ($node_type) { # node type must be entered warn "Wrong format of line $. in ".$network_cfg_file. "\n"; $error = 1; } else { # correct $node_types{$node_type} = 1; } } elsif (lc $kw eq 'ltra') { # link type with reciprocal link my (undef, $link_type, $reciprocal) = split /$config_files_separator/; if (not $link_type) { # link type must be entered warn "Link type not entered on line $. in ".$network_cfg_file. "\n"; $error = 1; } elsif (not $reciprocal) { # reciprocal link type must be entered warn "Reciprocal link type not entered on line $. in ".$network_cfg_file. "\n"; $error = 1; } else { # correct # adding the link type $link_types_to_numbers{$link_type} = $link_type_number; $link_type_number++; # adding the type of reciprocal link $link_types_to_numbers{$reciprocal} = $link_type_number; # storing information about reciprocity for automatic creation of links when reading network data $reciprocal_links{$link_type} = $link_type_number; $link_type_number++; } } elsif (lc $kw eq 'lt') { # link type my (undef, $link_type) = split /$config_files_separator/; if (not $link_type) { # link type must be entered warn "Link type not entered on line $. in ".$network_cfg_file. "\n"; $error = 1; } else { # correct $link_types_to_numbers{$link_type} = $link_type_number; $link_type_number++; } } else { warn "Incorrect row type in ".$network_cfg_file. ", line $.\n"; $error = 1; } } close F; } # checking network configuration file for correctness (link types) # - correct row format: # link weight: lw link_type_label weight # unless (open F, $network_cfg_file) { warn "Cannot open network configuration file $network_cfg_file.\n"; $error = 1; } else { while () { # skip comments and rows with whitespaces only next if /^#/ or /^\s*$/; # remove comments and new line characters s/#.*//; chomp; # determining the keyword my $kw = /(\w+)/ ? $& : ''; if (lc $kw eq 'lw') { # link weight my (undef, $link_type, $weight) = split /$config_files_separator/; if (not $link_type) { # link type must be entered warn "Link type not entered on line $. in ".$network_cfg_file. "\n"; $error = 1; } elsif (not exists $link_types_to_numbers{$link_type}) { warn "Non-existing link type on line $. in ".$network_cfg_file. "\n"; $error = 1; } elsif ($weight !~ /^\d+(.\d+)?$/) { # the value of $weight must be numeric warn "Non-numeric value $weight for link weight provided in ".$network_cfg_file. ", line $.\n"; $error = 1; } else { # correct, setting the link weight $link_types[$link_types_to_numbers{$link_type}] = 0+$weight; } } else { warn "Incorrect row type in ".$network_cfg_file. ", line $.\n"; $error = 1; } } close F; } # checking network configuration data for correctness: # - correct row format: # nodes: n node_label node_type importance # links: l init term type # - links must start end end in existing nodes # - nodes and links must have existing type unless (open F, $data_file) { warn "Cannot open data file $data_file.\n"; $error = 1; } else { $number_of_nodes = 0; $number_of_links = 0; our (@links); while () { # skip comments and rows with whitespaces only next if /^#/ or /^\s*$/; # remove comments and new line characters s/#.*//; chomp; # determining the keyword my $kw = /(\w+)/ ? $& : ''; if (lc $kw eq 'n') { # node my (undef, $label, $node_type, $imp) = split /$config_files_separator/; if (not ($label and $node_type and $imp)) { # node label, type and importance must be entered warn "Wrong format of line $. in ".$data_file. "\n"; $error = 1; } elsif (not $node_types{$node_type}) { # checking the type of the node warn "Non existing node type provided in ".$data_file. ", line $.\n"; $error = 1; } elsif ($imp !~ /^\d+(.\d+)?$/) { # the value of $imp must be numeric warn "Non-numeric value $imp for node importance provided in ".$data_file. ", line $.\n"; $error = 1; } else { # correct $nodes{$label} = $imp; # storing nodes using numbers, convertion through hash $node_labels_to_numbers # here we create thos hash $node_numbers_to_labels[$number_of_nodes] = $label unless $arg{-check}; $node_labels_to_numbers{$label} = $number_of_nodes++ unless $arg{-check}; } } elsif (lc $kw eq 'l') { # link my (undef, $init, $term, $link_type) = split /$config_files_separator/; if (not (defined $link_type and defined $init and defined $term)) { # link type, initial and terminal nodes must be entered warn "Wrong format of line $. in ".$data_file. "\n"; $error = 1; } elsif (not defined $link_types_to_numbers{$link_type}) { # checking the type of the link warn "Non existing link type $link_type provided in ".$data_file. ", line $.\n"; $error = 1; } elsif (not defined $nodes{$init}) { # non existing initial node warn "Initial node for link doesn's exists in ".$data_file. ", line $.\n"; $error = 1; } elsif (not defined $nodes{$term}) { # non existing terminal node warn "Terminal node for link doesn's exists in ".$data_file. ", line $.\n"; $error = 1; } else { # correct # storing the link as INITIAL -> {-term => NODE, -type => TYPE} push @{$links[$node_labels_to_numbers{$init}]}, [0+$node_labels_to_numbers{$term}, 0+$link_types_to_numbers{$link_type}] unless $arg{-check}; $number_of_links++; if (defined $reciprocal_links{$link_type}) { # reciprocal link is automatically created # storing the link as TERM -> [INITIAL, LTRA] push @{$links[$node_labels_to_numbers{$term}]}, [0+$node_labels_to_numbers{$init}, 0+$link_types_to_numbers{$reciprocal_links{$link_type}}] unless $arg{-check}; $number_of_links++; } } } else { # wrong row type warn "Incorrect row type in ".$data_file. ", line $.\n"; $error = 1; } } close F; } # checking initial activation data for correctness: # - correct row format: # ia node_label values # - all nodes don't have to exist unless (open F, $init_activation_file) { warn "Cannot open initial activation file $init_activation_file.\n"; $error = 1; } else { # setting total initial value to zero @total_init_activation = (); while () { # skip comments and rows with whitespaces only next if /^#/ or /^\s*$/; last if /__END__/; # remove comments and new line characters s/#.*//; chomp; # determining the keyword my $kw = /(\w+)/ ? $& : ''; if (lc $kw eq 'ia') { # initial activation for a node my (undef, $node, @values ) = split /$config_files_separator/; # TODO: checking whether the number of initialy set values is always the same if (not ($node and scalar @values)) { # node and value must be entered warn "Wrong format of line $. in ".$init_activation_file. "\n"; $error = 1; } elsif (not $nodes{$node}) { # non existing node # the warning just one for each non-existing node # we are tolerant to errors, when a non-existing node is activated warn "Node for activation doesn't exist in ".$init_activation_file. ", line $.\n"; } elsif (grep {$_ !~ /^\d+(.\d+)?$/} @values) { # the activation values must be numeric warn "Non-numeric value provided in ".$init_activation_file. ", line $.\n"; $error = 1; } else { # correct unless ($arg{-check}) { for (0..$#values) { # if a node was already activated, increase its value (cummulative level of activation) $active_nodes{$node_labels_to_numbers{$node}}->[$_] += $values[$_]; # calculating total initial activation for calibration $total_init_activation[$_] += $values[$_]; } } } } =pod } elsif (lc $kw eq 'na') { # noise activation my (undef, $node, $iteration ) = split /$config_files_separator/; push @{$noise_activation{$iteration}}, $node; } =cut # storing the list of initially activated nodes for calibration @list_of_initially_activated_nodes = keys %active_nodes; @sum_of_initially_activated_nodes = @total_init_activation; } close F; } =pod if ($arg{-check}) { # releasing unnecessary resources undef $config_files_separator, $beta, $IterationsNo, $alg_cfg -network_cfg -data $data_cfg, -init_activation -link_types_to_numbers $reciprocal_links, -link_types -links $node_types, -nodes -node_labels_to_numbers $node_numbers_to_labels, -active_nodes -config_files_set $number_of_links, -number_of_nodes/; } =cut # not needed any more undef %reciprocal_links; unless ($error) { # data are entered correctly $data_ok = 1; } # the structure of data needed for spreading activation # -link_types_to_numbers converting link types to numbers for optimization of storing information about nodes # -config_files_separator # -node_labels_to_numbers converting node labels to numbers for optimization of storing information about nodes # -links array describing links in the network, index of the element equals to the node number, # element contains list of terminal nodes in followinf format: [[LINK TYPE, TERMINAL NODE], [LINK TYPE, TERMINAL NODE]...] # -active_nodes hash with activated nodes with assigned values # -link_types array containing characterictics for each link type # -nodes hash containing nodes and associated types # -node_types hash with types of nodes # -number_of_links # -number_of_nodes } sub print_nodes { my @_links; for my $init (0..$#links) { # $init contains the number of initial node # array @{$links[$init]} contains the list of terminal nodes [TERM, TYPE], [TERM, TYPE]... push @_links, "['".node_label($init)."', '".node_label($_->[0])."']" for @{$links[$init]}; } return '(', join(', ', @_links), ')'; } 1;