AI-Pathfinding-OptimizeMultiple

 view release on metacpan or  search on metacpan

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN


use PDL;
use Scalar::Util qw/ blessed /;

has chosen_scans     => ( isa => 'ArrayRef', is => 'rw' );
has _iter_idx        => ( isa => 'Int', is => 'rw', default  => sub { 0; }, );
has _num_boards      => ( isa => 'Int', is => 'ro', init_arg => 'num_boards', );
has _orig_scans_data => ( isa => 'PDL', is => 'rw' );
has _optimize_for => ( isa => 'Str', is => 'ro', init_arg => 'optimize_for', );
has _scans_data   => ( isa => 'PDL', is => 'rw' );
has _selected_scans =>
    ( isa => 'ArrayRef', is => 'ro', init_arg => 'selected_scans', );
has _status => ( isa => 'Str',           is => 'rw' );
has _quotas => ( isa => 'ArrayRef[Int]', is => 'ro', init_arg => 'quotas' );
has _total_boards_solved => ( isa => 'Int', is => 'rw' );
has _total_iters         => ( is  => 'rw' );
has _trace_cb =>
    ( isa => 'Maybe[CodeRef]', is => 'ro', init_arg => 'trace_cb' );
has _scans_meta_data => ( isa => 'ArrayRef', is => 'ro', init_arg => 'scans' );
has _scans_iters_pdls =>
    ( isa => 'HashRef', is => 'rw', init_arg => 'scans_iters_pdls' );
has _stats_factors => (

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN

        map {
            my $id     = $_->id();
            my $pdl    = $self->_scans_iters_pdls()->{$id};
            my $factor = $self->_stats_factors->{$id};
            (
                defined($factor)
                ? ( ( $pdl >= 0 ) * ( ( $pdl / $factor )->ceil() ) +
                        ( $pdl < 0 ) * $pdl )
                : $pdl
            );
        } @{ $self->_selected_scans() }
    );

    $self->_orig_scans_data($scans_data);
    $self->_scans_data( $self->_orig_scans_data()->copy() );

    return 0;
}

my $BOARDS_DIM     = 0;
my $SCANS_DIM      = 1;

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN


    return _my_sum_over( $pdl->xchg( 0, 1 ) );
}

sub _get_iter_state_params_len
{
    my $self = shift;

    my $iters_quota        = 0;
    my $num_solved_in_iter = 0;
    my $selected_scan_idx;

    # If no boards were solved, then try with a larger quota
    while ( $num_solved_in_iter == 0 )
    {
        my $q_more = $self->_get_next_quota();
        if ( !defined($q_more) )
        {
            AI::Pathfinding::OptimizeMultiple::Error::OutOfQuotas->throw(
                error => "No q_more", );
        }

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN


        my $iters        = $self->_scans_data()->slice(":,:,0");
        my $solved       = ( ( $iters <= $iters_quota ) & ( $iters > 0 ) );
        my $num_moves    = $self->_scans_data->slice(":,:,2");
        my $solved_moves = $solved * $num_moves;

        my $solved_moves_sums   = _my_sum_over($solved_moves);
        my $solved_moves_counts = _my_sum_over($solved);
        my $solved_moves_avgs   = $solved_moves_sums / $solved_moves_counts;

        ( undef, undef, $selected_scan_idx, undef ) =
            $solved_moves_avgs->minmaximum();

        $num_solved_in_iter = $solved_moves_counts->at($selected_scan_idx);
    }

    return {
        quota      => $iters_quota,
        num_solved => $num_solved_in_iter,
        scan_idx   => $selected_scan_idx,
    };
}

sub _get_iter_state_params_minmax_len
{
    my $self = shift;

    my $iters_quota        = 0;
    my $num_solved_in_iter = 0;
    my $selected_scan_idx;

    # If no boards were solved, then try with a larger quota
    while ( $num_solved_in_iter == 0 )
    {
        my $q_more = $self->_get_next_quota();
        if ( !defined($q_more) )
        {
            AI::Pathfinding::OptimizeMultiple::Error::OutOfQuotas->throw(
                error => "No q_more", );
        }

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN

        $iters_quota += $q_more;

        my $iters        = $self->_scans_data()->slice(":,:,0");
        my $solved       = ( ( $iters <= $iters_quota ) & ( $iters > 0 ) );
        my $num_moves    = $self->_scans_data->slice(":,:,2");
        my $solved_moves = $solved * $num_moves;

        my $solved_moves_maxima = $solved_moves->maximum()->slice(":,(0),(0)");
        my $solved_moves_counts = _my_sum_over($solved);

        ( undef, undef, $selected_scan_idx, undef ) =
            $solved_moves_maxima->minmaximum();

        $num_solved_in_iter = $solved_moves_counts->at($selected_scan_idx);
    }

    return {
        quota      => $iters_quota,
        num_solved => $num_solved_in_iter,
        scan_idx   => $selected_scan_idx,
    };
}

sub _get_iter_state_params_speed
{
    my $self = shift;

    my $iters_quota        = 0;
    my $num_solved_in_iter = 0;
    my $selected_scan_idx;

    # If no boards were solved, then try with a larger quota
    while ( $num_solved_in_iter == 0 )
    {
        my $q_more = $self->_get_next_quota();
        if ( !defined($q_more) )
        {
            AI::Pathfinding::OptimizeMultiple::Error::OutOfQuotas->throw(
                error => "No q_more" );
        }

        $iters_quota += $q_more;

        ( undef, $num_solved_in_iter, undef, $selected_scan_idx ) =
            PDL::minmaximum(
            PDL::sumover(
                ( $self->_scans_data() <= $iters_quota ) &
                    ( $self->_scans_data() > 0 )
            )
            );
    }

    return {
        quota      => $iters_quota,
        num_solved => $num_solved_in_iter->at(0),
        scan_idx   => $selected_scan_idx->at(0),
    };
}

sub _get_selected_scan
{
    my $self = shift;

    my $iter_state =
        AI::Pathfinding::OptimizeMultiple::IterState->new(
        $self->_get_iter_state_params(), );

    $iter_state->attach_to($self);

    return $iter_state;
}

sub _inspect_quota
{
    my $self = shift;

    my $state = $self->_get_selected_scan();

    $state->register_params();

    $state->update_total_iters();

    if ( $self->_total_boards_solved() == $self->_num_boards() )
    {
        $self->_status("solved_all");
    }
    else

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN


sub _get_num_scans
{
    my $self = shift;

    return ( ( $self->_scans_data()->dims() )[$SCANS_DIM] );
}

sub _calc_chosen_scan
{
    my ( $self, $selected_scan_idx, $iters_quota ) = @_;

    return AI::Pathfinding::OptimizeMultiple::ScanRun->new(
        {
            iters => (
                $iters_quota * (
                    $self->_stats_factors->{
                        ( $self->_selected_scans->[$selected_scan_idx]->id() ),
                    } // 1
                )
            ),
            scan_idx => $selected_scan_idx,
        }
    );
}

sub calc_flares_meta_scan
{
    my $self = shift;

    $self->chosen_scans( [] );

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN


    my $next_num_iters_for_each_scan_x_scan =
        ( ( $ones_constant x $flares_num_iters ) );

    my $num_moves = $self->_scans_data->slice(":,:,1");

    # The number of moves for dimension 0,1,2 above.
    my $num_moves_repeat = $num_moves->clump( 1 .. 2 )->xchg( 0, 1 )
        ->dummy( 0, $self->_get_num_scans() );

    my $selected_scan_idx;

    my $loop_iter_num = 0;

    my $UNSOLVED_NUM_MOVES_CONSTANT = 64 * 1024 * 1024;

    my $last_avg = $UNSOLVED_NUM_MOVES_CONSTANT;

FLARES_LOOP:
    while ( my $q_more = $self->_get_next_quota() )
    {

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN

            $which_minima_are_solved * $minimal_num_moves_solved;

        my $solved_moves_sums   = _my_xchg_sum_over($minimal_with_zeroes);
        my $solved_moves_counts = _my_xchg_sum_over($which_minima_are_solved);
        my $solved_moves_avgs   = $solved_moves_sums / $solved_moves_counts;

        # print join(",", $solved_moves_avgs->minmaximum()), "\n";

        my $min_avg;

        ( $min_avg, undef, $selected_scan_idx, undef ) =
            $solved_moves_avgs->minmaximum();

        $last_avg = $min_avg;

        push @{ $self->chosen_scans() },
            $self->_calc_chosen_scan( $selected_scan_idx, $iters_quota );

        $flares_num_iters->set( $selected_scan_idx,
            $flares_num_iters->at($selected_scan_idx) + $iters_quota );
        $self->_selected_scans()->[$selected_scan_idx]->mark_as_used();

        $iters_quota = 0;

        my $num_solved = $solved_moves_counts->at($selected_scan_idx);

        my $flares_num_iters_repeat =
            $flares_num_iters->dummy( 0, $self->_num_boards() );

        # A boolean tensor:
        # Dimension 0 - board.
        # Dimension 1 - scans.
        my $solved_with_which_iter =
            ( $flares_num_iters_repeat >= $iters->clump( 1 .. 2 ) ) &
            ( $iters->clump( 1 .. 2 ) >= 0 );

lib/AI/Pathfinding/OptimizeMultiple.pm  view on Meta::CPAN

        {
            scans => \@scans,
            num_boards => 32_000,
            optimize_for => 'speed',
            scans_iters_pdls =>
            {
                first_search => $first_search_pdl,
                second_search => $second_search_pdl,
            },
            quotas => [400, 300, 200],
            selected_scans =>
            [
                AI::Pathfinding::OptimizeMultiple::Scan->new(
                    id => 'first_search',
                    cmd_line => "--preset first_search",
                ),
                AI::Pathfinding::OptimizeMultiple::Scan->new(
                    id => 'second_search',
                    cmd_line => "--preset second_search",
                ),
                AI::Pathfinding::OptimizeMultiple::Scan->new(

lib/AI/Pathfinding/OptimizeMultiple/App/CmdLine.pm  view on Meta::CPAN

            {
                do_rle        => $self->_should_rle_be_done(),
                offset_quotas => $self->_offset_quotas(),
            }
        )
    );

    return;
}

sub _selected_scans
{
    my $self = shift;

    return $self->_input_obj->selected_scans();
}

sub _map_all_but_last
{
    my $self = shift;

    my ( $cb, $arr_ref ) = (@_);

    return [
        ( map { $cb->($_) } @$arr_ref[ 0 .. $#$arr_ref - 1 ] ),

lib/AI/Pathfinding/OptimizeMultiple/App/CmdLine.pm  view on Meta::CPAN


sub _line_ends_mapping
{
    my $self = shift;
    return $self->_map_all_but_last( sub { "$_[0] \\\n" }, shift );
}

sub _get_used_scans
{
    my $self = shift;
    return [ grep { $_->is_used() } @{ $self->_selected_scans() } ];
}

sub _get_scan_line
{
    my ( $self, $line ) = @_;

    return
          $line->{'cmd_line'}
        . " -step 500 "
        . join( " ",

lib/AI/Pathfinding/OptimizeMultiple/App/CmdLine.pm  view on Meta::CPAN

    {
        return $quota;
    }
}

sub _map_scan_idx_to_id
{
    my $self  = shift;
    my $index = shift;

    return $self->_selected_scans()->[$index]->id();
}

sub _format_prelude_iter
{
    my $self = shift;

    my $iter = shift;

    return
          ( $self->_is_flares() ? "Run:" : "" )

lib/AI/Pathfinding/OptimizeMultiple/App/CmdLine.pm  view on Meta::CPAN

        : "get_scans_iters_pdls"
    );

    return $self->_input_obj->$method();
}

sub _arbitrator_trace_cb
{
    my $args = shift;
    printf( "%s \@ %s (%s solved)\n",
        @$args{qw(iters_quota selected_scan_idx total_boards_solved)} );
}

sub _init_arbitrator
{
    my $self = shift;

    return $self->_arbitrator(
        AI::Pathfinding::OptimizeMultiple->new(
            {
                'scans' => [
                    map { +{ name => $_->id() } }
                        @{ $self->_input_obj->_suitable_scans_list() },
                ],
                'quotas'           => $self->_get_quotas(),
                'selected_scans'   => $self->_selected_scans(),
                'num_boards'       => $self->_num_boards(),
                'scans_iters_pdls' => $self->_calc_scans_iters_pdls(),
                'trace_cb'         => \&_arbitrator_trace_cb,
                'optimize_for'     => $self->_optimize_for(),
                'stats_factors'    => $self->_stats_factors(),
            }
        )
    );
}

lib/AI/Pathfinding/OptimizeMultiple/DataInputObj.pm  view on Meta::CPAN


use File::Path qw(mkpath);

use AI::Pathfinding::OptimizeMultiple::Scan ();

use PDL              (qw( pdl ));
use PDL::IO::FastRaw (qw( readfraw writefraw ));

has start_board => ( isa => 'Int', is => 'ro', required => 1 );
has num_boards  => ( isa => 'Int', is => 'ro', required => 1 );
has selected_scans => (
    isa      => 'ArrayRef',
    is       => 'ro',
    required => 1,
    default  => sub {
        my ($self) = @_;

        return $self->_calc_selected_scan_list();
    },
    lazy => 1,
);

has _scan_ids_to_indexes => (
    isa     => 'HashRef[Int]',
    is      => 'ro',
    lazy    => 1,
    default => sub {
        my ($self) = @_;

lib/AI/Pathfinding/OptimizeMultiple/DataInputObj.pm  view on Meta::CPAN

{
    my $self = shift;

    my $filename = shift;

    my $text = _slurp($filename);

    return [ split( /[\n\r]+/, $text ) ];
}

# Number of selected scans.
sub _num_sel_scans
{
    my $self = shift;

    return scalar( @{ $self->selected_scans() } );
}

sub _gen_initial_scans_tensor
{
    my $self       = shift;
    my $extra_dims = shift || [];

    return zeroes( $self->num_boards(), $self->_num_sel_scans, @$extra_dims );
}

lib/AI/Pathfinding/OptimizeMultiple/DataInputObj.pm  view on Meta::CPAN

my $NUM_NUMBERS_IN_HEADER = 3;

my $HEADER_START_BOARD_IDX  = 0;
my $HEADER_NUM_BOARDS       = 1;
my $HEADER_ITERATIONS_LIMIT = 2;

sub _get_scans_data_helper
{
    my $self = shift;

    my $selected_scans = $self->selected_scans();

    my $start_board = $self->start_board();

    my $scans_data      = {};
    my $scans_lens_data = {};

    my $data_dir = ".data-proc";
    my $lens_dir = ".data-len-proc";

    mkpath( [ $data_dir, $lens_dir ] );

    foreach my $scan (@$selected_scans)
    {
        {
            my $dest_path = $data_dir . "/" . $scan->id();
            {
                if (
                    $self->_should_update(
                        $scan->data_file_path(), $dest_path
                    )
                    )
                {

lib/AI/Pathfinding/OptimizeMultiple/DataInputObj.pm  view on Meta::CPAN

}

sub _suitable_scans_list
{
    my $self = shift;

    return [ grep { $self->_is_scan_suitable($_) }
            @{ $self->_get_all_scans_list_from_file() } ];
}

sub _calc_selected_scan_list
{
    my $self = shift;

    return _filter_scans_based_on_black_list_ids(
        $self->_suitable_scans_list(),
        $self->_black_list_ids_list(),
    );
}

sub _get_next_id_file_path

lib/AI/Pathfinding/OptimizeMultiple/DataInputObj.pm  view on Meta::CPAN

    }
    close($from_cmd);
    close($fcs_out);
    close($fc_pro_out);
}

sub get_scan_ids_aref
{
    my $self = shift;

    return [ map { $_->id() } @{ $self->selected_scans } ];
}

sub lookup_scan_idx_based_on_id
{
    my ( $self, $scan_id ) = @_;

    my $idx = $self->_scan_ids_to_indexes->{$scan_id};
    if ( !defined($idx) )
    {
        die "Index '$idx' does not exist!";

lib/AI/Pathfinding/OptimizeMultiple/DataInputObj.pm  view on Meta::CPAN

Returns the hash ref mapping scan IDs/names to iteration PDLs.

=head2 my $hash_ref = $self->get_scans_lens_iters_pdls()

Returns the hash ref mapping scan IDs/names to iteration+lengths PDLs.

=head2 $self->num_boards()

The number of boards.

=head2 $self->selected_scans()

An accessor for the selected scans.

=head2 $self->start_board()

The index of the board to start from.

=head2 $self->time_scan()

Times a new scan.

=head2 $self->get_scan_ids_aref()

lib/AI/Pathfinding/OptimizeMultiple/IterState.pm  view on Meta::CPAN

    # in $state->update_total_iters().
    my $idx_slice = $state->idx_slice();
    $idx_slice .=
        ( ( $r > 0 ) * ( $r - $state->_quota() ) ) + ( ( $r < 0 ) * ($r) );
}

sub _mark_as_used
{
    my $state = shift;

    $state->_main()->_selected_scans()->[ $state->_scan_idx() ]->mark_as_used();

    return;
}

sub _add_chosen
{
    my $state = shift;

    push @{ $state->_main()->chosen_scans() }, $state->get_chosen_struct();

lib/AI/Pathfinding/OptimizeMultiple/IterState.pm  view on Meta::CPAN

    return;
}

sub _trace_wrapper
{
    my $state = shift;

    $state->_main()->_trace(
        {
            'iters_quota'         => $state->_quota(),
            'selected_scan_idx'   => $state->_scan_idx(),
            'total_boards_solved' => $state->_main()->_total_boards_solved(),
        }
    );

    return;
}

sub register_params
{
    my $state = shift;

t/optimize-multiple-full-test.t  view on Meta::CPAN

# TEST:$c=0;
sub test_based_on_data
{
    local $Test::Builder::Level = $Test::Builder::Level + 1;

    my ( $scans_aref, $quotas_aref, $want_results, $stats_factors, $blurb ) =
        @_;

    my $results_aref = [];

    my $selected_scans = [
        map {
            my $id = $_->{name};

            AI::Pathfinding::OptimizeMultiple::Scan->new(
                id       => $id,
                cmd_line => "-l $id",
            )
        } @{$scans_aref},
    ];

t/optimize-multiple-full-test.t  view on Meta::CPAN

        Carp::confess("num is not 1.");
    }

    my $obj = AI::Pathfinding::OptimizeMultiple->new(
        {
            scans      => [ map { +{ name => $_->{name} } } @$scans_aref, ],
            num_boards => $nums[0],
            scans_iters_pdls =>
                { map { $_->{name} => pdl( $_->{data} ), } @$scans_aref, },
            quotas         => $quotas_aref,
            selected_scans => $selected_scans,
            optimize_for   => "speed",
            ( $stats_factors ? ( stats_factors => $stats_factors ) : () ),
        }
    );

    $obj->calc_meta_scan();

    my @have = (
        map {
            +{



( run in 0.610 second using v1.01-cache-2.11-cpan-49f99fa48dc )