AI-SimulatedAnnealing

 view release on metacpan or  search on metacpan

t/annealing_tests.t  view on Meta::CPAN

use List::Util ("max", "min");

use AI::SimulatedAnnealing;
use Text::BSV::BsvFileReader;

# Probability enumeration:
package Probability;

our $ONE_FIFTH  = 5;
our $ONE_FOURTH = 4;
our $ONE_THIRD  = 3;
our $ONE_HALF   = 2;

# Redeclaration of the main package:
package main;

# Constants:
my $POUND     = "#";
my $SQ        = "'";
my $DQ        = "\"";
my $SEMICOLON = ";";
my $CR        = "\r";
my $LF        = "\n";
my $SPACE     = " ";
my $EMPTY     = "";
my $TRUE      = 1;
my $FALSE     = 0;

my $CYCLES_PER_TEMPERATURE = 1_250;

# Main script:

# Get the input file path:
my $bsv_file_path = $ARGV[0];

unless (scalar @ARGV) {
    die "ERROR:  No command-line argumment.  Please provide the path to a "
      . "valid BSV (or simple CSV) file containing market distances.\n";
} # end unless

# Create a reader for the BSV file:
my $bsv_file_reader;

eval {
    $bsv_file_reader = Text::BSV::BsvFileReader->new($bsv_file_path);
};

if ($EVAL_ERROR) {
    my $exception = $EVAL_ERROR;

    given ($exception->get_type()) {
        when ($Text::BSV::Exception::FILE_NOT_FOUND) {
            say STDERR "$DQ$bsv_file_path$DQ is not a valid file path.";
            exit(1);
        }
        when ($Text::BSV::Exception::IO_ERROR) {
            say STDERR "Couldn't open $DQ$bsv_file_path$DQ for reading.";
            exit(1);
        }
        when ($Text::BSV::Exception::INVALID_DATA_FORMAT) {
            say STDERR "Invalid BSV data:  " . $exception->get_message();
            exit(1);
        }
        default {
            say STDERR $exception->get_message();
            exit(1);
        } # end when
    } # end given
} # end if

# Generate a list of distances for each probability from the data in the
# BSV file:
my $field_names = $bsv_file_reader->get_field_names();
my @mapped_distances; # indexes 2-5 = Probability constants;
                      # values = references to number arrays

for my $p (2..5) {
    $mapped_distances[$p] = [];
} # next $p

unless ($field_names->[0] eq "Time"
  && $field_names->[1] =~ /$Probability::ONE_FIFTH\z/s
  && $field_names->[2] =~ /$Probability::ONE_FOURTH\z/s
  && $field_names->[3] =~ /$Probability::ONE_THIRD\z/s
  && $field_names->[4] =~ /$Probability::ONE_HALF\z/s) {
    die "ERROR:  The input file does not contain market-distance data in "
      . "the expected format.\n";
} # end unless

while ($bsv_file_reader->has_next()) {
    my $record;
    my $dex;

    eval {
        $record = $bsv_file_reader->get_record();
    };

    if ($EVAL_ERROR) {
        given ($EVAL_ERROR->get_type()) {
            when ($Text::BSV::Exception::INVALID_DATA_FORMAT) {
                die "ERROR:  Invalid BSV data:  "
                  . $EVAL_ERROR->get_message() . $LF;
            }
            default {
                die "ERROR:  " . $EVAL_ERROR->get_message() . $LF;
            } # end when
        } # end given
    } # end if

    $dex = $record->{"Time"} - 3;

    unless ($dex >= 0
      && $dex <= scalar($mapped_distances[$Probability::ONE_FIFTH])) {
        die "ERROR:  The input file does not contain market-distance data "
          . "in the expected format.\n";
    } # end unless

    for my $p (2..5) {
        push @{ $mapped_distances[$p] }, $record->{$field_names->[6 - $p]};
    } # next $p
} # end while

unless (scalar @{ $mapped_distances[$Probability::ONE_FIFTH] } == 61) {
    die "ERROR:  The input file does not contain the expected number of "
      . "records.\n";
} # end unless

# Perform simulated annealing to optimize the coefficients for each of the
# four probabilities, and then print the results to the console:
for my $p (2..5) {
    my $cost_function = cost_function_factory($mapped_distances[$p]);
    my $optimized_coefficients;
    my @number_specs;

    push @number_specs,
      {"LowerBound" =>  0.0, "UpperBound" => 3.0, "Precision" => 3};
    push @number_specs,
      {"LowerBound" => -1.0, "UpperBound" => 5.0, "Precision" => 3};
    push @number_specs,
      {"LowerBound" => -4.0, "UpperBound" => 0.0, "Precision" => 3};

    $optimized_coefficients = anneal(
      \@number_specs, $cost_function, $CYCLES_PER_TEMPERATURE);

    # Print the results for this probability to the console:
    say "\nProbability:  1/$p";
    printf("Coefficients:  a = %1.3f; b = %1.3f; c= %1.3f\n",
      $optimized_coefficients->[0],
      $optimized_coefficients->[1],
      $optimized_coefficients->[2]);
    say "Cost:  " . $cost_function->($optimized_coefficients);
} # next $p

# Perform an annealing test with integers that triggers brute-force analysis
# and uses an anonymous cost function that minimizes this sum:
#
#     (10 * abs(23 - val)) + (the total range of a, b, and c)
#
# where "val" is the result of following expression:
#
#     (a * (x ** 2)) + bx + c
#
# in which x = 3:
my $abc;
my @number_specs;

push @number_specs,
  {"LowerBound" =>  1.9, "UpperBound" => 4, "Precision" => 0};
push @number_specs,
  {"LowerBound" =>  0.0, "UpperBound" => 2, "Precision" => 0};
push @number_specs,
  {"LowerBound" => -4.0, "UpperBound" => 8, "Precision" => 0};

$abc = anneal(\@number_specs,



( run in 1.892 second using v1.01-cache-2.11-cpan-39bf76dae61 )