######################### -*- Mode: Perl -*- #########################
##
## File          : $Basename: Binary.pm $
##
## Author        : Norbert Goevert
## Created On    : Thu Dec 10 16:01:28 1998
## Last Modified : Time-stamp: <2000-11-15 23:10:24 goevert>
##
## Description   : LSP approach for binary relevance scales
##
## $Id: Binary.pm 1.4 Wed, 15 Nov 2000 23:12:27 +0100 goevert $
## $ProjectHeader: LSP 2.6 Wed, 29 Nov 2000 09:57:57 +0100 goevert $
##
######################################################################


use strict;


=pod #---------------------------------------------------------------#

=head1 NAME

LSP::Binary - LSP approach for binary relevance scales

=head1 SYNOPSIS

  require LSP::Binary;
  $LSP = new LSP $dimension, @polynomial;
  $LSP->add_vector($vector, $scalar1, $scalar2);
  $LSP = function LSP::Binary @polynomial, $coefficients;

=head1 DESCRIPTION

LSP::Binary implements the LSP approach for binary relevance scales.
Therefore solutions consist of a single vector of coefficients. See
LSP(3) for further documentation. The only differences are that the
dimension of the relevancescale must not be specified within the
constructor and that you don't specifie the judge parameter within the
add_vector method. Instead you specify the number of negativ and
positive occurances of a given feature vector.

=head1 METHODS

See LSP(3). In addition:

=over

=cut #---------------------------------------------------------------#


package LSP::Binary;


use base qw(LSP);


our $VERSION;
'$ProjectVersion: 2.6 $ ' =~ /(\d+)\.(\d+)/; $VERSION = sprintf '%d.%03d', $1, $2;


## public ############################################################

sub new {

  my $proto = shift;
  my $class = ref($proto) || $proto;
  my $self  = $class->SUPER::new(1, @_);

  $self->{vec_vy} = (new Math::Matrix [ (0) x $self->{components} ])->transpose;

  bless $self => $class;
}


=pod #---------------------------------------------------------------#

=item $lsp->function(@polynomial, $coefficients);

If you've computed coefficients already and just want to use the
probability method to compute function values for specific feature
vectors you can use this method as a constructor. The @polynomial is
given the same way like in the C<new> constructor, $coefficients is an
array reference holding the coefficients of your regression function.

=cut #---------------------------------------------------------------#

sub function {

  my $proto = shift;
  my $class = ref($proto) || $proto;

  my $self = {};

  $self->{solution} = (Math::Matrix->new(pop))->transpose;
  $self->{polynomial} = [ @_ ]; # empty if linear function
  if (@{$self->{polynomial}}) {
    $self->{dimension} = scalar @{$self->{polynomial}->[0]};
  } else {
    # linear function
    $self->{dimension} = scalar @{$self->{solution}} - 1;
  }

  bless $self => $class;

  $self->_create_function;

  return $self;
}


sub solve {

  my $self = shift;

  my $result = $self->SUPER::solve;
  $self->_create_function;

  return $result;
}


sub probability {

  my $self = shift;

  &{$self->{_function}}(@_);
}


sub add_vector {

  my $self = shift;
  my($vector, $scalar1, $scalar2) = @_;

  return $self->_fail("Wrong dimension of vector ( @$vector ).")
    if @$vector ne $self->{dimension};

  # compute values of polynomial components
  my $newvec = $self->_components($vector);

  my $vec_v   = new Math::Matrix [ @$newvec ];
  my $vec_v_t = $vec_v->transpose;
  my $vec_v_v = $vec_v_t->multiply($vec_v);

  if ($scalar1 == 1) {
    $self->{momental}->add_to($vec_v_v->concat($vec_v_t));
  } elsif ($scalar1 != 0) {
    $self->{momental}->add_to($vec_v_v->concat($vec_v_t)->scalarproduct($scalar1));
  }
  if ($scalar2 == 1) {
    $self->{momental}->add_to($vec_v_v->concat($self->{vec_vy}));
  } elsif ($scalar2 != 0) {
    $self->{momental}->add_to($vec_v_v->scalarproduct($scalar2)->concat($self->{vec_vy}));
  }
}


## private ###########################################################

sub _create_function {

  my $self = shift; 

  my $string = '';
  foreach my $i (0 .. $#{$self->{polynomial}}) {
    my $comp = '';
    if ($self->{solution}->[$i]->[0] == 1) {
      $comp = '+ ';
    } elsif ($self->{solution}->[$i]->[0] < 0) {
      $comp = '- ' . abs($self->{solution}->[$i]->[0]) . ' * ';
    } elsif ($self->{solution}->[$i]->[0] > 0) {
      $comp = '+ ' . $self->{solution}->[$i]->[0] . ' * ';
    } else {
      next;
    }
    foreach my $j (0 .. $#{$self->{polynomial}->[$i]}) {
      if ($self->{polynomial}->[$i]->[$j] == 1) {
        $comp .= "\$v->[$j] * ";
      } elsif ($self->{polynomial}->[$i]->[$j]) {
        $comp .= "\$v->[$j]**$self->{polynomial}->[$i]->[$j] * ";
      }
    }
    $comp =~ s/ \* $//;
    next unless $comp;
    $string .= $comp . "\n  ";
  }

  $string =~ s/^\+ //;
  $string =~ s/\n  $/;/;
  $string = "sub {\n\n  my \$v = shift;\n\n  " . $string . "\n}\n";
  
  #print $string;
  
  $self->{_function} = eval $string;
}


=pod #---------------------------------------------------------------#

=back

=head1 BUGS

Yes. Please let me know!

=head1 SEE ALSO

LSP::Linear(3),
LSP::Binary(3),
LSP::Binary::Linear(3),
Math::Matrix(3),
perl(1).

=head1 AUTHOR

Norbert GE<ouml>vert E<lt>F<goevert@ls6.cs.uni-dortmund.de>E<gt>

=cut #---------------------------------------------------------------#


1;
__END__
