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