liboutputfunctions.cpp
1 /********************************************************************************
2  * Neural Network Framework. *
3  * Copyright (C) 2005-2009 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "liboutputfunctions.h"
21 #include "cluster.h"
22 #include <QStringList>
23 #include <QRegExp>
24 #include <cmath>
25 
26 namespace farsa {
27 
29  : OutputFunction() {
30 }
31 
33  outputs.copyValues( inputs );
34 }
35 
36 bool IdentityFunction::derivate( const DoubleVector&, const DoubleVector&, DoubleVector& derivates ) const {
37  derivates.setAll( 1.0 );
38  return true;
39 }
40 
42 {
43  ((void)params);
44  ((void)prefix);
45  // Nothing to configure
46 }
47 
48 void IdentityFunction::save(ConfigurationParameters& params, QString prefix)
49 {
50  // Just telling our type to ConfigurationParameters
51  params.startObjectParameters(prefix, "IdentityFunction", this);
52 }
53 
55  : OutputFunction() {
56  this->rate = rate;
57 }
58 
59 void ScaleFunction::apply( DoubleVector& inputs, DoubleVector& outputs ) {
60  mul( outputs, rate, inputs );
61 }
62 
63 bool ScaleFunction::derivate( const DoubleVector&, const DoubleVector&, DoubleVector& derivates ) const {
64  derivates.setAll( rate );
65  return true;
66 }
67 
69 {
70  rate = 1.0;
71  QString str = params.getValue(prefix + "rate");
72  if (!str.isEmpty()) {
73  bool ok;
74  rate = str.toDouble(&ok);
75  if (!ok) {
76  rate = 1.0;
77  }
78  }
79 }
80 
81 void ScaleFunction::save(ConfigurationParameters& params, QString prefix)
82 {
83  params.startObjectParameters(prefix, "ScaleFunction", this);
84  params.createParameter(prefix, "rate", QString::number(rate));
85 }
86 
88  : OutputFunction() {
89  gainv = gain;
90 }
91 
92 void GainFunction::apply( DoubleVector& inputs, DoubleVector& outputs ) {
93  outputs.copyValues( inputs );
94  outputs += gainv;
95 }
96 
97 bool GainFunction::derivate( const DoubleVector&, const DoubleVector&, DoubleVector& derivates ) const {
98  derivates.setAll( 1.0 );
99  return true;
100 }
101 
103 {
104  gainv = 1.0;
105  QString str = params.getValue(prefix + "gain");
106  if (!str.isEmpty()) {
107  bool ok;
108  gainv = str.toDouble(&ok);
109  if (!ok) {
110  gainv = 1.0;
111  }
112  }
113 }
114 
115 void GainFunction::save(ConfigurationParameters& params, QString prefix)
116 {
117  params.startObjectParameters(prefix, "GainFunction", this);
118  params.createParameter(prefix, "gain", QString::number(gainv));
119 }
120 
122  lambda = l;
123 }
124 
126  // ____________1_________________
127  // exp( -lamba*inputs ) + 1
128  inv( exp( mul( outputs, -lambda, inputs ) ) += 1.0 );
129 }
130 
131 bool SigmoidFunction::derivate( const DoubleVector&, const DoubleVector& outputs, DoubleVector& derivates ) const {
132  // derivates <- lambda * out * (1.0-out)
133  subtract( derivates, 1.0, outputs );
134  derivates *= outputs;
135  derivates *= lambda;
136  return true;
137 }
138 
140 {
141  lambda = 1.0;
142  QString str = params.getValue(prefix + "lambda");
143  if (!str.isEmpty()) {
144  bool ok;
145  lambda = str.toDouble(&ok);
146  if (!ok) {
147  lambda = 1.0;
148  }
149  }
150 }
151 
152 void SigmoidFunction::save(ConfigurationParameters& params, QString prefix)
153 {
154  params.startObjectParameters(prefix, "SigmoidFunction", this);
155  params.createParameter(prefix, "lambda", QString::number(lambda));
156 }
157 
159  : OutputFunction() {
160  lambda = l;
161 }
162 
164  unsigned int size = inputs.size();
165  double x;
166  double x0 = 6. + 2./3.;
167  double zero = 0.5;
168  for ( unsigned int i = 0; i<size; i++ ) {
169  x = inputs[i];
170  x *= lambda;
171  x -= (.5 - zero) / (.075 + zero);
172  if ( x <= -x0 ) {
173  outputs[i] = 0.0;
174  } else {
175  if ( x < x0 ) {
176  outputs[i] = .5 + .575 * x / ( 1.0 + fabs(x) );
177  } else {
178  outputs[i] = 1.0;
179  }
180  }
181  }
182 }
183 
184 bool FakeSigmoidFunction::derivate( const DoubleVector&, const DoubleVector& outputs, DoubleVector& derivates ) const {
185  // derivates <- lambda * out * (1.0-out)
186  subtract( derivates, 1.0, outputs );
187  derivates *= outputs;
188  derivates *= lambda;
189  return true;
190 }
191 
193 {
194  lambda = 1.0;
195  QString str = params.getValue(prefix + "lambda");
196  if (!str.isEmpty()) {
197  bool ok;
198  lambda = str.toDouble(&ok);
199  if (!ok) {
200  lambda = 1.0;
201  }
202  }
203 }
204 
206 {
207  params.startObjectParameters(prefix, "FakeSigmoidFunction", this);
208  params.createParameter(prefix, "lambda", QString::number(lambda));
209 }
210 
211 ScaledSigmoidFunction::ScaledSigmoidFunction( double l, double min, double max )
212  : OutputFunction() {
213  lambda = l;
214  this->min = min;
215  this->max = max;
216 }
217 
219  //--- compute the sigmoid
220  // ____________1_________________
221  // exp( -lamba*inputs ) + 1
222  inv( exp( mul( outputs, -lambda, inputs ) ) += 1.0 );
223  //--- and scale it
224  outputs *= max-min;
225  outputs += min;
226 }
227 
228 bool ScaledSigmoidFunction::derivate( const DoubleVector&, const DoubleVector& outputs, DoubleVector& derivates ) const {
229  // derivates <- lambda * out * (1.0-out)
230  subtract( derivates, 1.0, outputs );
231  derivates *= outputs;
232  derivates *= lambda;
233  return true;
234 }
235 
237 {
238  lambda = 1.0;
239  QString str = params.getValue(prefix + "lambda");
240  if (!str.isEmpty()) {
241  bool ok;
242  lambda = str.toDouble(&ok);
243  if (!ok) {
244  lambda = 1.0;
245  }
246  }
247 
248  min = -1.0;
249  str = params.getValue(prefix + "min");
250  if (!str.isEmpty()) {
251  bool ok;
252  min = str.toDouble(&ok);
253  if (!ok) {
254  min = -1.0;
255  }
256  }
257 
258  max = 1.0;
259  str = params.getValue(prefix + "max");
260  if (!str.isEmpty()) {
261  bool ok;
262  max = str.toDouble(&ok);
263  if (!ok) {
264  max = 1.0;
265  }
266  }
267 }
268 
270 {
271  params.startObjectParameters(prefix, "ScaledSigmoidFunction", this);
272  params.createParameter(prefix, "lambda", QString::number(lambda));
273  params.createParameter(prefix, "min", QString::number(min));
274  params.createParameter(prefix, "max", QString::number(max));
275 }
276 
278  : OutputFunction() {
279  min_x = 0.0;
280  max_x = 0.0;
281  min_y = 0.0;
282  max_y = 0.0;
283 }
284 
285 RampFunction::RampFunction( double minX, double maxX, double minY, double maxY )
286  : OutputFunction() {
287  min_x = minX;
288  max_x = maxX;
289  min_y = minY;
290  max_y = maxY;
291 }
292 
293 void RampFunction::apply( DoubleVector& inputs, DoubleVector& outputs ) {
294  unsigned int size = inputs.size();
295  double m = ( max_y-min_y )/( max_x-min_x );
296  double q = min_y - m*min_x;
297  for ( unsigned int i = 0; i<size; i++ ) {
298  double ret = m*(inputs[i]) + q;
299  if (ret < min_y) {
300  outputs[i] = min_y;
301  } else if (ret > max_y) {
302  outputs[i] = max_y;
303  } else {
304  outputs[i] = ret;
305  }
306  }
307 }
308 
309 bool RampFunction::derivate( const DoubleVector& inputs, const DoubleVector&, DoubleVector& derivates ) const {
310  for( unsigned int i=0; i<inputs.size(); i++ ) {
311  if ( inputs[i] >= min_x && inputs[i] <= max_x ) {
312  derivates[i] = ( max_y-min_y )/( max_x-min_x );
313  } else {
314  double y;
315  y = 1.0/( 1.0 + std::exp( -inputs[i] ) );
316  derivates[i] = y * ( 1.0 - y );
317  }
318  }
319  return true;
320 }
321 
323 {
324  min_x = 0.0;
325  QString str = params.getValue(prefix + "minX");
326  if (!str.isEmpty()) {
327  bool ok;
328  min_x = str.toDouble(&ok);
329  if (!ok) {
330  min_x = 0.0;
331  }
332  }
333 
334  max_x = 0.0;
335  str = params.getValue(prefix + "maxX");
336  if (!str.isEmpty()) {
337  bool ok;
338  max_x = str.toDouble(&ok);
339  if (!ok) {
340  max_x = 0.0;
341  }
342  }
343 
344  min_y = 0.0;
345  str = params.getValue(prefix + "minY");
346  if (!str.isEmpty()) {
347  bool ok;
348  min_y = str.toDouble(&ok);
349  if (!ok) {
350  min_y = 0.0;
351  }
352  }
353 
354  max_y = 0.0;
355  str = params.getValue(prefix + "maxY");
356  if (!str.isEmpty()) {
357  bool ok;
358  max_y = str.toDouble(&ok);
359  if (!ok) {
360  max_y = 0.0;
361  }
362  }
363 }
364 
365 void RampFunction::save(ConfigurationParameters& params, QString prefix)
366 {
367  params.startObjectParameters(prefix, "RampFunction", this);
368  params.createParameter(prefix, "minX", QString::number(min_x));
369  params.createParameter(prefix, "maxX", QString::number(max_x));
370  params.createParameter(prefix, "minY", QString::number(min_y));
371  params.createParameter(prefix, "maxY", QString::number(max_y));
372 }
373 
375  : OutputFunction() {
376  m = 0.0;
377  b = 0.0;
378 }
379 
380 LinearFunction::LinearFunction( double m, double b )
381  : OutputFunction() {
382  this->m = m;
383  this->b = b;
384 }
385 
387  mul( outputs, m, inputs ) += b;
388 }
389 
390 bool LinearFunction::derivate( const DoubleVector& , const DoubleVector&, DoubleVector& derivates ) const {
391  derivates.setAll( m );
392  return true;
393 }
394 
396 {
397  m = 0.0;
398  QString str = params.getValue(prefix + "m");
399  if (!str.isEmpty()) {
400  bool ok;
401  m = str.toDouble(&ok);
402  if (!ok) {
403  m = 0.0;
404  }
405  }
406 
407  b = 0.0;
408  str = params.getValue(prefix + "b");
409  if (!str.isEmpty()) {
410  bool ok;
411  b = str.toDouble(&ok);
412  if (!ok) {
413  b = 0.0;
414  }
415  }
416 }
417 
418 void LinearFunction::save(ConfigurationParameters& params, QString prefix)
419 {
420  params.startObjectParameters(prefix, "LinearFunction", this);
421  params.createParameter(prefix, "m", QString::number(m));
422  params.createParameter(prefix, "b", QString::number(b));
423 }
424 
425 StepFunction::StepFunction( double min, double max, double threshold )
426  : OutputFunction() {
427  this->min = min;
428  this->max = max;
429  this->threshold = threshold;
430 }
431 
432 void StepFunction::apply( DoubleVector& inputs, DoubleVector& outputs ) {
433  unsigned int size = inputs.size();
434  for ( unsigned int i = 0; i<size; i++ ) {
435  ( inputs[i] > threshold ) ? outputs[i] = max : outputs[i] = min;
436  }
437 }
438 
439 bool StepFunction::derivate( const DoubleVector& inputs, const DoubleVector&, DoubleVector& derivates ) const {
440  //--- return the same as if it is a sigmoid with lambda = 1
441  for( unsigned int i=0; i<inputs.size(); i++ ) {
442  double y;
443  y = 1.0/( 1.0 + std::exp( -inputs[i] ) );
444  derivates[i] = y * ( 1.0 - y );
445  }
446  return true;
447 }
448 
450 {
451  min = 0.0;
452  QString str = params.getValue(prefix + "min");
453  if (!str.isEmpty()) {
454  bool ok;
455  min = str.toDouble(&ok);
456  if (!ok) {
457  min = 0.0;
458  }
459  }
460 
461  max = 1.0;
462  str = params.getValue(prefix + "max");
463  if (!str.isEmpty()) {
464  bool ok;
465  max = str.toDouble(&ok);
466  if (!ok) {
467  max = 1.0;
468  }
469  }
470 
471  threshold = 0.0;
472  str = params.getValue(prefix + "threshold");
473  if (!str.isEmpty()) {
474  bool ok;
475  threshold = str.toDouble(&ok);
476  if (!ok) {
477  threshold = 0.0;
478  }
479  }
480 }
481 
482 void StepFunction::save(ConfigurationParameters& params, QString prefix)
483 {
484  params.startObjectParameters(prefix, "StepFunction", this);
485  params.createParameter(prefix, "min", QString::number(min));
486  params.createParameter(prefix, "max", QString::number(max));
487  params.createParameter(prefix, "threshold", QString::number(threshold));
488 }
489 
491  : OutputFunction(), delta(), outprev()
492 {
493 }
494 
496  : OutputFunction(), delta(d.size()), outprev(d.size()) {
497  delta.copyValues( d );
498  outprev.zeroing();
499 }
500 
502  //--- y <- delta*y(t-1) + (1.0-delta)*inputs
503  //--- its equivalent to
504  //--- y <- delta*( y(t-1) - inputs ) + inputs
505  outputs = subtract( outputs, outprev, inputs );
506  outputs *= delta;
507  outputs += inputs;
508  outprev.copyValues( outputs );
509 }
510 
512  outprev.zeroing();
513 }
514 
516  if ( clusterv->numNeurons() != delta.size() ) {
519  }
520 }
521 
523 {
524  // Delta is a vector, that is a list of space-separated values
525  QString str = params.getValue(prefix + "delta");
526  if (!str.isEmpty()) {
527  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
528  delta.resize(list.size());
529  for( int i = 0; i < list.size(); i++) {
530  bool ok;
531  delta[i] = list[i].toDouble(&ok);
532  if (!ok) {
533  delta[i] = 0.0;
534  }
535  }
536  }
537 
538  // Also reloading outprev (resizing it to match delta length)
539  str = params.getValue(prefix + "outprev");
540  if (!str.isEmpty()) {
541  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
542  outprev.resize(list.size());
543  for( int i = 0; i < list.size(); i++) {
544  bool ok;
545  outprev[i] = list[i].toDouble(&ok);
546  if (!ok) {
547  outprev[i] = 0.0;
548  }
549  }
550  }
552 }
553 
555 {
556  params.startObjectParameters(prefix, "LeakyIntegratorFunction", this);
557 
558  // First creating a string list, then transforming to a single string
559  QStringList list;
560  for (unsigned int i = 0; i < delta.size(); i++) {
561  list.push_back(QString::number(delta[i]));
562  }
563  params.createParameter(prefix, "delta", list.join(" "));
564 
565  // Saving in the same way also outprev
566  list.clear();
567  for (unsigned int i = 0; i < delta.size(); i++) {
568  list.push_back(QString::number(outprev[i]));
569  }
570  params.createParameter(prefix, "outprev", list.join(" "));
571 }
572 
573 LogLikeFunction::LogLikeFunction( double A, double B )
574  : OutputFunction() {
575  this->A = A;
576  this->B = B;
577 }
578 
580  //--- y <- x / ( 1+A*x+b )
581  outputs = inv( mul( outputs, A, inputs ) += (1.0+B) );
582  outputs *= inputs;
583 }
584 
586 {
587  A = 1.0;
588  QString str = params.getValue(prefix + "A");
589  if (!str.isEmpty()) {
590  bool ok;
591  A = str.toDouble(&ok);
592  if (!ok) {
593  A = 1.0;
594  }
595  }
596 
597  B = 5.0;
598  str = params.getValue(prefix + "B");
599  if (!str.isEmpty()) {
600  bool ok;
601  B = str.toDouble(&ok);
602  if (!ok) {
603  B = 5.0;
604  }
605  }
606 }
607 
608 void LogLikeFunction::save(ConfigurationParameters& params, QString prefix)
609 {
610  params.startObjectParameters(prefix, "LogLikeFunction", this);
611  params.createParameter(prefix, "A", QString::number(A));
612  params.createParameter(prefix, "B", QString::number(B));
613 }
614 
616  : OutputFunction(), first(), second(), mid() {
617 }
618 
620  : OutputFunction(), first(f), second(g), mid() {
621 }
622 
624  // auto_ptr will release memory for us
625 }
626 
628  first->apply( inputs, mid );
629  second->apply( mid, outputs );
630 }
631 
633  first.reset(f);
634  first->setCluster( clusterv );
635  return true;
636 }
637 
639  return first.get();
640 }
641 
643  second.reset(g);
644  second->setCluster( clusterv );
645  return true;
646 }
647 
649  return second.get();
650 }
651 
653  mid.resize( clusterv->numNeurons() );
654  first->setCluster( clusterv );
655  second->setCluster( clusterv );
656 }
657 
659 {
660  // We don't need configured component functions here (and they will be
661  // configured after exiting from this function)
662  first.reset(params.getObjectFromParameter<OutputFunction>(prefix + "first", false, false));
663  second.reset(params.getObjectFromParameter<OutputFunction>(prefix + "second", false, false));
664 
665  // We don't need to reload a reference to the cluster as he calls our setCluster
666  // function after our creation
667 }
668 
670 {
671  params.startObjectParameters(prefix, "CompositeFunction", this);
672  params.createParameter(prefix, "first", first.get());
673  params.createParameter(prefix, "second", second.get());
674 
675  // We don't need to save the reference to the cluster as he calls our setCluster
676  // function after our creation
677 }
678 
680  : OutputFunction(), first(), second(), mid()
681 {
682  this->w1 = 0.0;
683  this->w2 = 0.0;
684 }
685 
687  : OutputFunction(), first(f), second(g), mid() {
688  this->w1 = w1;
689  this->w2 = w2;
690 }
691 
693  // auto_ptr will release memory for us
694 }
695 
697  first->apply( inputs, mid );
698  mid *= w1;
699  second->apply( inputs, outputs );
700  outputs *= w2;
701  outputs += mid;
702 }
703 
705  first.reset(f);
706  first->setCluster( clusterv );
707  return true;
708 }
709 
711  return first.get();
712 }
713 
715  w1 = v;
716  return true;
717 }
718 
720  return w1;
721 }
722 
724  second.reset(g);
725  second->setCluster( clusterv );
726  return true;
727 }
728 
730  return second.get();
731 }
732 
734  w2 = v;
735  return true;
736 }
737 
739  return w2;
740 }
741 
743  mid.resize( clusterv->numNeurons() );
744  first->setCluster( clusterv );
745  second->setCluster( clusterv );
746 }
747 
749 {
750  // We don't need configured component functions here (and they will be
751  // configured after exiting from this function)
752  first.reset(params.getObjectFromParameter<OutputFunction>(prefix + "first", false, false));
753 
754  w1 = 0.0;
755  QString str = params.getValue(prefix + "w1");
756  if (!str.isEmpty()) {
757  bool ok;
758  w1 = str.toDouble(&ok);
759  if (!ok) {
760  w1 = 0.0;
761  }
762  }
763 
764  second.reset(params.getObjectFromParameter<OutputFunction>(prefix + "second", false, false));
765 
766  w2 = 0.0;
767  str = params.getValue(prefix + "w2");
768  if (!str.isEmpty()) {
769  bool ok;
770  w2 = str.toDouble(&ok);
771  if (!ok) {
772  w2 = 0.0;
773  }
774  }
775 
776  // We don't need to reload a reference to the cluster as he calls our setCluster
777  // function after our creation
778 }
779 
781 {
782  params.startObjectParameters(prefix, "LinearComboFunction", this);
783  params.createParameter(prefix, "first", first.get());
784  params.createParameter(prefix, "w1", QString::number(w1));
785  params.createParameter(prefix, "second", second.get());
786  params.createParameter(prefix, "w2", QString::number(w2));
787 
788  // We don't need to save the reference to the cluster as he calls our setCluster
789  // function after our creation
790 }
791 
792 }