1 /*
2  * scrm is an implementation of the Sequential-Coalescent-with-Recombination Model.
3  *
4  * Copyright (C) 2013, 2014 Paul R. Staab, Sha (Joe) Zhu, Dirk Metzler and Gerton Lunter
5  *
6  * This file is part of scrm.
7  *
8  * scrm is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #include "fastfunc.h"
24 #include "mersenne_twister.h"
25 
26 
speedtest(int oper,double rate,double growth,double limit)27 double speedtest(int oper, double rate, double growth, double limit) {
28 
29   int i;
30   double x = 1.0;
31   float xf = 1.0;
32   double y = 0.999;
33   float yf = 0.0;
34   double z = 0.0;
35   float zf = 0.0;
36 
37   class MersenneTwister rg;
38   class FastFunc ff;
39 
40   for (i=0; i<100000000; i++) {
41     x += 1e-8;
42     xf = float(x);
43     y = x;
44     switch(oper) {
45     case 0: break;
46     case 1: y = x+y; break;
47     case 2: y = x/x; break;
48     case 3: y = x*x; break;
49     case 4: y = log(x); break;
50     case 5: yf = logf(xf); break;
51     case 6: y = ff.fastlog(x); break;
52     case 7: y = exp(x); break;
53     case 8: y = ff.fastexp_lo(x); break;
54     case 9: y = rg.sample(); break;
55     case 10: y = rg.sampleExpo( 1.0 ); break;
56     case 11: y = rg.sampleExpoLimit( 1.0, 0.1 ); break;
57     default: y = rg.sampleExpoExpoLimit( rate, growth, limit ); break;
58     }
59     z += y;
60     zf += yf;
61   }
62   return z + zf;  // to avoid optimizing everything away
63 }
64 
65 //
66 // set of unit tests
67 //
68 
ut_calclog(double x,char func,class FastFunc & ff)69 double ut_calclog( double x, char func, class FastFunc& ff ) {
70 
71   switch (func) {
72   case 'l':
73     return log(x);
74   case 'f':
75     return (double)logf( (float)x );
76   case 'F':
77     return ff.fastlog(x);
78   default:
79     std::cout << "Bad function identifier: " << func << std::endl;
80     exit(1);
81   }
82 }
83 
84 // test that log is in range around 1.0
unittest_range(char func,class FastFunc & ff)85 bool unittest_range( char func, class FastFunc& ff) {
86 
87   double x,y;
88   // test log 1.0
89   y = ut_calclog( 1.0, func, ff );
90   if (y == 0.0) {
91     std::cout << " ok : log(1.0) = 0.0 " << std::endl;
92   } else {
93     std::cout << " PROBLEM : log(1.0) = " << y << std::endl;
94     return false;
95   }
96 
97   // test numbers very close to 1.0
98   int i_limit = 1564;  // float
99   if (func == 'd') i_limit = 3574;
100   for (int i = 0; i<10000; i++) {
101     x = exp( -(1+i/100.0) );
102     y = ut_calclog( 1.0 - x, func, ff );
103     if ( y >= 0.0 ) {
104       std::cout << (i >= i_limit ? " ok" : " PROBLEM") << " : log( 1.0 - " << x << " ) = " << y << " at i=" << i << std::endl;
105       return i >= i_limit;
106     }
107     y = ut_calclog( 1.0 + x, func, ff );
108     if ( y <= 0.0 ) {
109       std::cout << (i >= i_limit ? " ok" : " PROBLEM") << " : log( 1.0 + " << x << " ) = " << y << " at i=" << i << std::endl;
110       return i >= i_limit;
111     }
112   }
113   return true;  // never happens
114 }
115 
unittest_maxdiff_monotone(char func,class FastFunc & ff)116 bool unittest_maxdiff_monotone( char func, class FastFunc& ff) {
117 
118   double epsilon = 1.5e-7;
119   double maxdiff = 0.0;
120   double y0 = ut_calclog( 0.5 - epsilon, func, ff );
121   for (int i=0; i<(int)(2+1.0/epsilon); i++) {
122     double x = 0.5 + epsilon*i;
123     double y1 = log(x);
124     double y2 = ut_calclog( x, func, ff );
125     double diff = fabs(y1-y2);
126     if (diff > maxdiff) {
127       maxdiff = diff;
128     }
129     if (y2 <= y0) {
130       std::cout << " PROBLEM: not increasing at " << x << " (i=" << i << ")" << std::endl;
131       return false;
132     }
133     y0 = y2;
134   }
135   std::cout << " max abs diff across [0.5-1.5] = " << maxdiff << std::endl;
136 }
137 
138 
unittest_log()139 void unittest_log() {
140 
141   class FastFunc ff;
142   for (int i=0; i<3; i++) {
143     char func = "lfF"[i];
144     std::cout << "Testing: " << func << "  [ l=log(dbl); f=log(float); F=fastlog(dbl) ]" << std::endl;
145     unittest_range( func, ff );
146     unittest_maxdiff_monotone( func, ff );
147   }
148 }
149 
150 
speedtest_log()151 void speedtest_log() {
152 
153   const int CASES=4;
154   const double epsilon=1e-7;
155   const double x[CASES] = {0.0, 1.0, 2.0, 3.0};
156   class FastFunc ff;
157   int i;
158   printf("x\t\tlog\t\tfastlog\n");
159   for (i=0; i<CASES; i++) {
160     printf("%1.9f\t%1.9f\t%1.9f\n", x[i], log(x[i]), ff.fastlog(x[i]));
161   }
162   double maxdiff = 0.0;
163   for (i=0; i<(int)(2+1.0/epsilon); i++) {
164     double y = 0.5 + epsilon*i;
165     double z1 = log(y);
166     double z2 = ff.fastlog(y);
167     double diff = fabs(z1-z2);
168     if (diff > maxdiff) {
169       maxdiff = diff;
170     }
171   }
172   std::cout << "max absolute difference across [0.5-1.5] = " << maxdiff << std::endl;
173 }
174 
175 
main(int argc,char ** argv)176 int main(int argc, char** argv) {
177 
178   const int CASES=10;
179   const int LLTEST=12;
180   const char* testnames[LLTEST+1] = {"nop\t","+\t","/\t","*\t","log()\t","logf()\t","fastlog()","exp()\t","fastexp()","raw sample","sample(1.0)","sample(1.0,0.1)","sample\t"};
181   const double rate[CASES] =   {1.0,      1.0, 1.0, 1.0,      1.0, 1.0, 10.0, 1.0,      1.0,  1.0};
182   const double growth[CASES] = {0.0,      0.0, 0.0, 1.0,      1.0, 1.0, 10.0, -1.0,     -1.0, -1.0};
183   const double limit[CASES] =  {INFINITY, 1.0, 0.1, INFINITY, 1.0, 0.1, 0.1,  INFINITY, 1.0,  0.1};
184 
185   class MersenneTwister rg;
186 
187   unittest_log();
188 
189   speedtest_log();
190 
191   printf("Test\t\tRate\tGrowth\tLimit\tTime\n");
192   for (int i=0; i<LLTEST + CASES; i++) {
193     clock_t start = clock(), diff;
194     double r = i>=LLTEST ? rate[i-LLTEST] : 0, g = i>=LLTEST ? growth[i-LLTEST] : 0, l = i>=LLTEST ? limit[i-LLTEST] : 0;
195     speedtest(i, r, g, l);
196     diff = clock() - start;
197     printf("%s\t%1.4f\t%1.4f\t%1.4f\t%ld\n",testnames[i>=LLTEST ? LLTEST : i],r,g,l,diff * 1000 / CLOCKS_PER_SEC);
198   }
199   return 0;
200 }
201 
202