OpenCLExpressionUtilities.cpp 18 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* -------------------------------------------------------------------------- *
 *                                   OpenMM                                   *
 * -------------------------------------------------------------------------- *
 * This is part of the OpenMM molecular simulation toolkit originating from   *
 * Simbios, the NIH National Center for Physics-Based Simulation of           *
 * Biological Structures at Stanford, funded under the NIH Roadmap for        *
 * Medical Research, grant U54 GM072970. See https://simtk.org.               *
 *                                                                            *
 * Portions copyright (c) 2009 Stanford University and the Authors.           *
 * Authors: Peter Eastman                                                     *
 * Contributors:                                                              *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify       *
 * it under the terms of the GNU Lesser General Public License as published   *
 * by the Free Software Foundation, either version 3 of the License, or       *
 * (at your option) any later version.                                        *
 *                                                                            *
 * This program is distributed in the hope that it will be useful,            *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU Lesser General Public License for more details.                        *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.      *
 * -------------------------------------------------------------------------- */

#include "OpenCLExpressionUtilities.h"
#include "openmm/OpenMMException.h"
#include "lepton/Operation.h"

using namespace OpenMM;
using namespace Lepton;
using namespace std;

Peter Eastman's avatar
Peter Eastman committed
35
string OpenCLExpressionUtilities::doubleToString(double value) {
36
37
38
39
40
41
    stringstream s;
    s.precision(8);
    s << scientific << value << "f";
    return s.str();
}

Peter Eastman's avatar
Peter Eastman committed
42
string OpenCLExpressionUtilities::intToString(int value) {
43
44
45
46
47
    stringstream s;
    s << value;
    return s.str();
}

48
49
50
string OpenCLExpressionUtilities::createExpressions(const map<string, ParsedExpression>& expressions, const map<string, string>& variables,
        const vector<pair<string, string> >& functions, const string& prefix, const string& functionParams) {
    stringstream out;
51
52
53
    vector<ParsedExpression> allExpressions;
    for (map<string, ParsedExpression>::const_iterator iter = expressions.begin(); iter != expressions.end(); ++iter)
        allExpressions.push_back(iter->second);
54
55
    vector<pair<ExpressionTreeNode, string> > temps;
    for (map<string, ParsedExpression>::const_iterator iter = expressions.begin(); iter != expressions.end(); ++iter) {
56
        processExpression(out, iter->second.getRootNode(), temps, variables, functions, prefix, functionParams, allExpressions);
57
58
59
        out << iter->first << getTempName(iter->second.getRootNode(), temps) << ";\n";
    }
    return out.str();
60
61
}

62
void OpenCLExpressionUtilities::processExpression(stringstream& out, const ExpressionTreeNode& node, vector<pair<ExpressionTreeNode, string> >& temps,
63
64
        const map<string, string>& variables, const vector<pair<string, string> >& functions, const string& prefix, const string& functionParams,
        const vector<ParsedExpression>& allExpressions) {
65
66
67
68
    for (int i = 0; i < (int) temps.size(); i++)
        if (temps[i].first == node)
            return;
    for (int i = 0; i < (int) node.getChildren().size(); i++)
69
        processExpression(out, node.getChildren()[i], temps, variables, functions, prefix, functionParams, allExpressions);
70
    string name = prefix+intToString(temps.size());
71
    bool hasRecordedNode = false;
72
73
    
    out << "float " << name << " = ";
74
75
    switch (node.getOperation().getId()) {
        case Operation::CONSTANT:
76
77
            out << doubleToString(dynamic_cast<const Operation::Constant*>(&node.getOperation())->getValue());
            break;
78
79
80
81
82
        case Operation::VARIABLE:
        {
            map<string, string>::const_iterator iter = variables.find(node.getOperation().getName());
            if (iter == variables.end())
                throw OpenMMException("Unknown variable in expression: "+node.getOperation().getName());
83
84
85
86
87
88
89
90
91
92
            out << iter->second;
            break;
        }
        case Operation::CUSTOM:
        {
            int i;
            for (i = 0; i < (int) functions.size() && functions[i].first != node.getOperation().getName(); i++)
                ;
            if (i == functions.size())
                throw OpenMMException("Unknown function in expression: "+node.getOperation().getName());
93
            bool isDeriv = (dynamic_cast<const Operation::Custom*>(&node.getOperation())->getDerivOrder()[0] == 1);
94
            out << "0.0f;\n";
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
            temps.push_back(make_pair(node, name));
            hasRecordedNode = true;

            // If both the value and derivative of the function are needed, it's faster to calculate them both
            // at once, so check to see if both are needed.

            const ExpressionTreeNode* valueNode = NULL;
            const ExpressionTreeNode* derivNode = NULL;
            for (int j = 0; j < (int) allExpressions.size(); j++)
                findRelatedTabulatedFunctions(node, allExpressions[j].getRootNode(), valueNode, derivNode);
            string valueName = name;
            string derivName = name;
            if (valueNode != NULL && derivNode != NULL) {
                string name2 = prefix+intToString(temps.size());
                out << "float " << name2 << " = 0.0f;\n";
                if (isDeriv) {
                    valueName = name2;
                    temps.push_back(make_pair(*valueNode, name2));
                }
                else {
                    derivName = name2;
                    temps.push_back(make_pair(*derivNode, name2));
                }
            }
119
120
121
122
123
            out << "{\n";
            out << "float4 params = " << functionParams << "[" << i << "];\n";
            out << "float x = " << getTempName(node.getChildren()[0], temps) << ";\n";
            out << "if (x >= params.x && x <= params.y) {\n";
            out << "int index = (int) (floor((x-params.x)*params.z));\n";
124
            out << "index = min(index, (int) params.w);\n";
125
126
            out << "float4 coeff = " << functions[i].second << "[index];\n";
            out << "x = (x-params.x)*params.z-index;\n";
127
128
129
130
            if (valueNode != NULL)
                out << valueName << " = coeff.x+x*(coeff.y+x*(coeff.z+x*coeff.w));\n";
            if (derivNode != NULL)
                out << derivName << " = (coeff.y+x*(2.0f*coeff.z+x*3.0f*coeff.w))*params.z;\n";
131
132
133
            out << "}\n";
            out << "}";
            break;
134
135
        }
        case Operation::ADD:
136
137
            out << getTempName(node.getChildren()[0], temps) << "+" << getTempName(node.getChildren()[1], temps);
            break;
138
        case Operation::SUBTRACT:
139
140
            out << getTempName(node.getChildren()[0], temps) << "-" << getTempName(node.getChildren()[1], temps);
            break;
141
        case Operation::MULTIPLY:
142
143
            out << getTempName(node.getChildren()[0], temps) << "*" << getTempName(node.getChildren()[1], temps);
            break;
144
        case Operation::DIVIDE:
145
146
            out << getTempName(node.getChildren()[0], temps) << "/" << getTempName(node.getChildren()[1], temps);
            break;
147
        case Operation::POWER:
148
149
            out << "pow(" << getTempName(node.getChildren()[0], temps) << ", " << getTempName(node.getChildren()[1], temps) << ")";
            break;
150
        case Operation::NEGATE:
151
152
            out << "-" << getTempName(node.getChildren()[0], temps);
            break;
153
        case Operation::SQRT:
154
155
            out << "sqrt(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
156
        case Operation::EXP:
157
            out << "EXP(" << getTempName(node.getChildren()[0], temps) << ")";
158
            break;
159
        case Operation::LOG:
160
            out << "LOG(" << getTempName(node.getChildren()[0], temps) << ")";
161
            break;
162
        case Operation::SIN:
163
164
            out << "sin(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
165
        case Operation::COS:
166
167
            out << "cos(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
168
        case Operation::SEC:
169
170
            out << "1.0f/cos(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
171
        case Operation::CSC:
172
173
            out << "1.0f/sin(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
174
        case Operation::TAN:
175
176
            out << "tan(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
177
        case Operation::COT:
178
179
            out << "1.0f/tan(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
180
        case Operation::ASIN:
181
182
            out << "asin(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
183
        case Operation::ACOS:
184
185
            out << "acos(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
186
        case Operation::ATAN:
187
188
            out << "atan(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
189
190
191
192
193
194
195
196
197
        case Operation::SINH:
            out << "sinh(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
        case Operation::COSH:
            out << "cosh(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
        case Operation::TANH:
            out << "tanh(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
198
199
200
201
202
203
        case Operation::ERF:
            out << "erf(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
        case Operation::ERFC:
            out << "erfc(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
204
205
206
        case Operation::STEP:
            out << getTempName(node.getChildren()[0], temps) << " >= 0.0f ? 1.0f : 0.0f";
            break;
207
        case Operation::SQUARE:
208
209
210
211
212
        {
            string arg = getTempName(node.getChildren()[0], temps);
            out << arg << "*" << arg;
            break;
        }
213
        case Operation::CUBE:
214
215
216
217
218
        {
            string arg = getTempName(node.getChildren()[0], temps);
            out << arg << "*" << arg << "*" << arg;
            break;
        }
219
        case Operation::RECIPROCAL:
220
            out << "RECIP(" << getTempName(node.getChildren()[0], temps) << ")";
221
            break;
222
        case Operation::ADD_CONSTANT:
223
224
            out << doubleToString(dynamic_cast<const Operation::AddConstant*>(&node.getOperation())->getValue()) << "+" << getTempName(node.getChildren()[0], temps);
            break;
225
        case Operation::MULTIPLY_CONSTANT:
226
227
            out << doubleToString(dynamic_cast<const Operation::MultiplyConstant*>(&node.getOperation())->getValue()) << "*" << getTempName(node.getChildren()[0], temps);
            break;
228
        case Operation::POWER_CONSTANT:
229
230
231
232
233
234
        {
            double exponent = dynamic_cast<const Operation::PowerConstant*>(&node.getOperation())->getValue();
            if (exponent == 0.0)
                out << "1.0f";
            else if (exponent == (int) exponent) {
                out << "0.0f;\n";
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
                temps.push_back(make_pair(node, name));
                hasRecordedNode = true;

                // If multiple integral powers of the same base are needed, it's faster to calculate all of them
                // at once, so check to see if others are also needed.

                map<int, const ExpressionTreeNode*> powers;
                powers[(int) exponent] = &node;
                for (int j = 0; j < (int) allExpressions.size(); j++)
                    findRelatedPowers(node, allExpressions[j].getRootNode(), powers);
                vector<int> exponents;
                vector<string> names;
                vector<bool> hasAssigned(powers.size(), false);
                exponents.push_back((int) fabs(exponent));
                names.push_back(name);
                for (map<int, const ExpressionTreeNode*>::const_iterator iter = powers.begin(); iter != powers.end(); ++iter) {
                    if (iter->first != exponent) {
252
                        exponents.push_back(iter->first >= 0 ? iter->first : -iter->first);
253
254
255
256
257
258
                        string name2 = prefix+intToString(temps.size());
                        names.push_back(name2);
                        temps.push_back(make_pair(*iter->second, name2));
                        out << "float " << name2 << " = 0.0f;\n";
                    }
                }
259
260
                out << "{\n";
                out << "float multiplier = " << (exponent < 0.0 ? "1.0f/" : "") << getTempName(node.getChildren()[0], temps) << ";\n";
261
262
263
                bool done = false;
                while (!done) {
                    done = true;
264
                    for (int i = 0; i < (int) exponents.size(); i++) {
265
266
267
268
269
270
271
272
273
274
                        if (exponents[i]%2 == 1) {
                            if (!hasAssigned[i])
                                out << names[i] << " = multiplier;\n";
                            else
                                out << names[i] << " *= multiplier;\n";
                            hasAssigned[i] = true;
                        }
                        exponents[i] >>= 1;
                        if (exponents[i] != 0)
                            done = false;
275
                    }
276
                    if (!done)
277
278
279
280
281
282
                        out << "multiplier *= multiplier;\n";
                }
                out << "}";
            }
            else
                out << "pow(" << getTempName(node.getChildren()[0], temps) << ", " << doubleToString(exponent) << ")";
283
            break;
284
        }
285
286
287
288
289
290
291
292
293
        case Operation::MIN:
            out << "min(" << getTempName(node.getChildren()[0], temps) << ", " << getTempName(node.getChildren()[1], temps) << ")";
            break;
        case Operation::MAX:
            out << "max(" << getTempName(node.getChildren()[0], temps) << ", " << getTempName(node.getChildren()[1], temps) << ")";
            break;
        case Operation::ABS:
            out << "fabs(" << getTempName(node.getChildren()[0], temps) << ")";
            break;
294
295
        default:
            throw OpenMMException("Internal error: Unknown operation in user-defined expression: "+node.getOperation().getName());
296
    }
297
    out << ";\n";
298
299
    if (!hasRecordedNode)
        temps.push_back(make_pair(node, name));
300
301
302
303
304
305
306
307
308
}

string OpenCLExpressionUtilities::getTempName(const ExpressionTreeNode& node, const vector<pair<ExpressionTreeNode, string> >& temps) {
    for (int i = 0; i < (int) temps.size(); i++)
        if (temps[i].first == node)
            return temps[i].second;
    stringstream out;
    out << "Internal error: No temporary variable for expression node: " << node;
    throw OpenMMException(out.str());
309
}
310
311
312
313
314
315
316
317
318
319
320
321
322

void OpenCLExpressionUtilities::findRelatedTabulatedFunctions(const ExpressionTreeNode& node, const ExpressionTreeNode& searchNode,
            const ExpressionTreeNode*& valueNode, const ExpressionTreeNode*& derivNode) {
    if (searchNode.getOperation().getId() == Operation::CUSTOM && node.getChildren()[0] == searchNode.getChildren()[0]) {
        if (dynamic_cast<const Operation::Custom*>(&searchNode.getOperation())->getDerivOrder()[0] == 0)
            valueNode = &searchNode;
        else
            derivNode = &searchNode;
    }
    else
        for (int i = 0; i < (int) searchNode.getChildren().size(); i++)
            findRelatedTabulatedFunctions(node, searchNode.getChildren()[i], valueNode, derivNode);
}
323
324
325

void OpenCLExpressionUtilities::findRelatedPowers(const ExpressionTreeNode& node, const ExpressionTreeNode& searchNode, map<int, const ExpressionTreeNode*>& powers) {
    if (searchNode.getOperation().getId() == Operation::POWER_CONSTANT && node.getChildren()[0] == searchNode.getChildren()[0]) {
326
327
328
329
        double realPower = dynamic_cast<const Operation::PowerConstant*>(&searchNode.getOperation())->getValue();
        int power = (int) realPower;
        if (power != realPower)
            return; // We are only interested in integer powers.
330
331
332
333
334
335
336
337
338
339
        if (powers.find(power) != powers.end())
            return; // This power is already in the map.
        if (powers.begin()->first*power < 0)
            return; // All powers must have the same sign.
        powers[power] = &searchNode;
    }
    else
        for (int i = 0; i < (int) searchNode.getChildren().size(); i++)
            findRelatedPowers(node, searchNode.getChildren()[i], powers);
}
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354

vector<mm_float4> OpenCLExpressionUtilities::computeFunctionCoefficients(const vector<double>& values, bool interpolating) {
    // First create a padded set of function values.

    vector<double> padded(values.size()+2);
    padded[0] = 2*values[0]-values[1];
    for (int i = 0; i < (int) values.size(); i++)
        padded[i+1] = values[i];
    padded[padded.size()-1] = 2*values[values.size()-1]-values[values.size()-2];

    // Now compute the spline coefficients.

    vector<mm_float4> f(values.size()-1);
    for (int i = 0; i < (int) values.size()-1; i++) {
        if (interpolating)
355
            f[i] = mm_float4((cl_float) padded[i+1],
356
357
                                (cl_float) (0.5*(-padded[i]+padded[i+2])),
                                (cl_float) (0.5*(2.0*padded[i]-5.0*padded[i+1]+4.0*padded[i+2]-padded[i+3])),
358
                                (cl_float) (0.5*(-padded[i]+3.0*padded[i+1]-3.0*padded[i+2]+padded[i+3])));
359
        else
360
            f[i] = mm_float4((cl_float) ((padded[i]+4.0*padded[i+1]+padded[i+2])/6.0),
361
362
                                (cl_float) ((-3.0*padded[i]+3.0*padded[i+2])/6.0),
                                (cl_float) ((3.0*padded[i]-6.0*padded[i+1]+3.0*padded[i+2])/6.0),
363
                                (cl_float) ((-padded[i]+3.0*padded[i+1]-3.0*padded[i+2]+padded[i+3])/6.0));
364
365
366
    }
    return f;
}